diff options
Diffstat (limited to 'subprojects/viatra-runtime/src/main')
247 files changed, 23714 insertions, 1 deletions
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java index ebae57a7..58285845 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java | |||
@@ -9,7 +9,6 @@ | |||
9 | 9 | ||
10 | package tools.refinery.viatra.runtime.api; | 10 | package tools.refinery.viatra.runtime.api; |
11 | 11 | ||
12 | import org.eclipse.emf.common.notify.Notifier; | ||
13 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; | 12 | import tools.refinery.viatra.runtime.api.scope.IBaseIndex; |
14 | import tools.refinery.viatra.runtime.api.scope.QueryScope; | 13 | import tools.refinery.viatra.runtime.api.scope.QueryScope; |
15 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | 14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; |
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java new file mode 100644 index 00000000..d0367cde --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java | |||
@@ -0,0 +1,226 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.counting; | ||
11 | |||
12 | import java.util.List; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; | ||
16 | import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; | ||
17 | import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; | ||
18 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
19 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; | ||
20 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
21 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
22 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; | ||
23 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
26 | |||
27 | /** | ||
28 | * This class is the optimized implementation of the Counting algorithm. | ||
29 | * | ||
30 | * @author Tamas Szabo | ||
31 | * | ||
32 | * @param <V> | ||
33 | * the type parameter of the nodes in the graph data source | ||
34 | */ | ||
35 | public class CountingAlg<V> implements IGraphObserver<V>, ITcDataSource<V> { | ||
36 | |||
37 | private CountingTcRelation<V> tc = null; | ||
38 | private IBiDirectionalGraphDataSource<V> gds = null; | ||
39 | private List<ITcObserver<V>> observers; | ||
40 | |||
41 | /** | ||
42 | * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data | ||
43 | * source. Attach itself on the graph data source as an observer. | ||
44 | * | ||
45 | * @param gds | ||
46 | * the graph data source instance | ||
47 | */ | ||
48 | public CountingAlg(IGraphDataSource<V> gds) { | ||
49 | |||
50 | if (gds instanceof IBiDirectionalGraphDataSource<?>) { | ||
51 | this.gds = (IBiDirectionalGraphDataSource<V>) gds; | ||
52 | } else { | ||
53 | this.gds = new IBiDirectionalWrapper<V>(gds); | ||
54 | } | ||
55 | |||
56 | observers = CollectionsFactory.<ITcObserver<V>>createObserverList(); | ||
57 | tc = new CountingTcRelation<V>(true); | ||
58 | |||
59 | initTc(); | ||
60 | gds.attachObserver(this); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * Initializes the transitive closure relation. | ||
65 | */ | ||
66 | private void initTc() { | ||
67 | this.setTcRelation(CountingTcRelation.createFrom(gds)); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public void edgeInserted(V source, V target) { | ||
72 | if (!source.equals(target)) { | ||
73 | deriveTc(source, target, true); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public void edgeDeleted(V source, V target) { | ||
79 | if (!source.equals(target)) { | ||
80 | deriveTc(source, target, false); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public void nodeInserted(V n) { | ||
86 | |||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public void nodeDeleted(V n) { | ||
91 | this.tc.deleteTupleEnd(n); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Derives the transitive closure relation when an edge is inserted or deleted. | ||
96 | * | ||
97 | * @param source | ||
98 | * the source of the edge | ||
99 | * @param target | ||
100 | * the target of the edge | ||
101 | * @param dCount | ||
102 | * the value is -1 if an edge was deleted and +1 if an edge was inserted | ||
103 | */ | ||
104 | private void deriveTc(V source, V target, boolean isInsertion) { | ||
105 | |||
106 | // if (dCount == 1 && isReachable(target, source)) { | ||
107 | // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!"); | ||
108 | // } | ||
109 | |||
110 | CountingTcRelation<V> dtc = new CountingTcRelation<V>(false); | ||
111 | Set<V> tupEnds = null; | ||
112 | |||
113 | // 1. d(tc(x,y)) :- d(l(x,y)) | ||
114 | if (tc.updateTuple(source, target, isInsertion)) { | ||
115 | dtc.updateTuple(source, target, true /* deltas implicitly have the same sign as isInsertion*/); | ||
116 | notifyTcObservers(source, target, isInsertion); | ||
117 | } | ||
118 | |||
119 | // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y) | ||
120 | tupEnds = tc.getTupleEnds(target); | ||
121 | if (tupEnds != null) { | ||
122 | for (V tupEnd : tupEnds) { | ||
123 | if (!tupEnd.equals(source)) { | ||
124 | if (tc.updateTuple(source, tupEnd, isInsertion)) { | ||
125 | dtc.updateTuple(source, tupEnd, true /* deltas implicitly have the same sign as isInsertion*/); | ||
126 | notifyTcObservers(source, tupEnd, isInsertion); | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | |||
132 | // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y)) | ||
133 | CountingTcRelation<V> newTuples = dtc; | ||
134 | CountingTcRelation<V> tmp = null; | ||
135 | dtc = new CountingTcRelation<V>(false); | ||
136 | |||
137 | IMemoryView<V> nodes = null; | ||
138 | |||
139 | while (!newTuples.isEmpty()) { | ||
140 | |||
141 | tmp = dtc; | ||
142 | dtc = newTuples; | ||
143 | newTuples = tmp; | ||
144 | newTuples.clear(); | ||
145 | |||
146 | for (V tS : dtc.getTupleStarts()) { | ||
147 | nodes = gds.getSourceNodes(tS); | ||
148 | for (V nS : nodes.distinctValues()) { | ||
149 | int count = nodes.getCount(nS); | ||
150 | for (int i = 0; i < count; i++) { | ||
151 | tupEnds = dtc.getTupleEnds(tS); | ||
152 | if (tupEnds != null) { | ||
153 | for (V tT : tupEnds) { | ||
154 | if (!nS.equals(tT)) { | ||
155 | if (tc.updateTuple(nS, tT, isInsertion)) { | ||
156 | newTuples.updateTuple(nS, tT, true /* deltas implicitly have the same sign as isInsertion*/); | ||
157 | notifyTcObservers(nS, tT, isInsertion); | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | // System.out.println(tc); | ||
168 | } | ||
169 | |||
170 | public ITcRelation<V> getTcRelation() { | ||
171 | return this.tc; | ||
172 | } | ||
173 | |||
174 | public void setTcRelation(CountingTcRelation<V> tc) { | ||
175 | this.tc = tc; | ||
176 | } | ||
177 | |||
178 | @Override | ||
179 | public boolean isReachable(V source, V target) { | ||
180 | return tc.containsTuple(source, target); | ||
181 | } | ||
182 | |||
183 | @Override | ||
184 | public void attachObserver(ITcObserver<V> to) { | ||
185 | this.observers.add(to); | ||
186 | |||
187 | } | ||
188 | |||
189 | @Override | ||
190 | public void detachObserver(ITcObserver<V> to) { | ||
191 | this.observers.remove(to); | ||
192 | } | ||
193 | |||
194 | @Override | ||
195 | public Set<V> getAllReachableTargets(V source) { | ||
196 | return tc.getTupleEnds(source); | ||
197 | } | ||
198 | |||
199 | @Override | ||
200 | public Set<V> getAllReachableSources(V target) { | ||
201 | return tc.getTupleStarts(target); | ||
202 | } | ||
203 | |||
204 | private void notifyTcObservers(V source, V target, boolean isInsertion) { | ||
205 | if (isInsertion) { | ||
206 | for (ITcObserver<V> o : observers) { | ||
207 | o.tupleInserted(source, target); | ||
208 | } | ||
209 | } else { | ||
210 | for (ITcObserver<V> o : observers) { | ||
211 | o.tupleDeleted(source, target); | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | |||
216 | @Override | ||
217 | public void dispose() { | ||
218 | tc.clear(); | ||
219 | this.gds.detachObserver(this); | ||
220 | } | ||
221 | |||
222 | @Override | ||
223 | public IGraphPathFinder<V> getPathFinder() { | ||
224 | return new DFSPathFinder<V>(gds, this); | ||
225 | } | ||
226 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java new file mode 100644 index 00000000..44abbc3d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java | |||
@@ -0,0 +1,259 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.counting; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; | ||
17 | import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; | ||
18 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
24 | |||
25 | /** | ||
26 | * Transitive closure relation implementation for the Counting algorithm. | ||
27 | * | ||
28 | * @author Tamas Szabo | ||
29 | * | ||
30 | * @param <V> | ||
31 | */ | ||
32 | public class CountingTcRelation<V> implements ITcRelation<V> { | ||
33 | |||
34 | private IMultiLookup<V, V> tuplesForward = null; | ||
35 | private IMultiLookup<V, V> tuplesBackward = null; | ||
36 | |||
37 | protected CountingTcRelation(boolean backwardIndexing) { | ||
38 | tuplesForward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); | ||
39 | if (backwardIndexing) | ||
40 | tuplesBackward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); | ||
41 | } | ||
42 | |||
43 | protected boolean isEmpty() { | ||
44 | return 0 == this.tuplesForward.countKeys(); | ||
45 | } | ||
46 | |||
47 | protected void clear() { | ||
48 | this.tuplesForward.clear(); | ||
49 | |||
50 | if (tuplesBackward != null) { | ||
51 | this.tuplesBackward.clear(); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | protected void union(CountingTcRelation<V> rA) { | ||
56 | IMultiLookup<V, V> rForward = rA.tuplesForward; | ||
57 | for (V source : rForward.distinctKeys()) { | ||
58 | IMemoryView<V> targetBag = rForward.lookup(source); | ||
59 | for (V target : targetBag.distinctValues()) { | ||
60 | this.addTuple(source, target, targetBag.getCount(target)); | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | public int getCount(V source, V target) { | ||
66 | IMemoryView<V> bucket = tuplesForward.lookup(source); | ||
67 | return bucket == null ? 0 : bucket.getCount(target); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false | ||
72 | * otherwise (in this case count is incremented with the given count parameter). | ||
73 | * | ||
74 | * @param source | ||
75 | * the source of the tuple | ||
76 | * @param target | ||
77 | * the target of the tuple | ||
78 | * @param count | ||
79 | * the count of the tuple, must be positive | ||
80 | * @return true if the relation did not contain previously the tuple | ||
81 | */ | ||
82 | public boolean addTuple(V source, V target, int count) { | ||
83 | if (tuplesBackward != null) { | ||
84 | tuplesBackward.addPairPositiveMultiplicity(target, source, count); | ||
85 | } | ||
86 | |||
87 | ChangeGranularity change = | ||
88 | tuplesForward.addPairPositiveMultiplicity(source, target, count); | ||
89 | |||
90 | return change != ChangeGranularity.DUPLICATE; | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * Derivation count of the tuple (source,target) is incremented or decremented. | ||
95 | * Returns true iff updated to / from zero derivation count. | ||
96 | * @since 1.7 | ||
97 | */ | ||
98 | public boolean updateTuple(V source, V target, boolean isInsertion) { | ||
99 | if (isInsertion) { | ||
100 | if (tuplesBackward != null) { | ||
101 | tuplesBackward.addPair(target, source); | ||
102 | } | ||
103 | ChangeGranularity change = | ||
104 | tuplesForward.addPair(source, target); | ||
105 | return change != ChangeGranularity.DUPLICATE; | ||
106 | } else { | ||
107 | if (tuplesBackward != null) { | ||
108 | tuplesBackward.removePair(target, source); | ||
109 | } | ||
110 | ChangeGranularity change = | ||
111 | tuplesForward.removePair(source, target); | ||
112 | return change != ChangeGranularity.DUPLICATE; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | public void deleteTupleEnd(V deleted) { | ||
117 | Set<V> sourcesToDelete = CollectionsFactory.createSet(); | ||
118 | Set<V> targetsToDelete = CollectionsFactory.createSet(); | ||
119 | |||
120 | for (V target : tuplesForward.lookupOrEmpty(deleted).distinctValues()) { | ||
121 | targetsToDelete.add(target); | ||
122 | } | ||
123 | if (tuplesBackward != null) { | ||
124 | for (V source : tuplesBackward.lookupOrEmpty(deleted).distinctValues()) { | ||
125 | sourcesToDelete.add(source); | ||
126 | } | ||
127 | } else { | ||
128 | for (V sourceCandidate : tuplesForward.distinctKeys()) { | ||
129 | if (tuplesForward.lookupOrEmpty(sourceCandidate).containsNonZero(deleted)) | ||
130 | sourcesToDelete.add(sourceCandidate); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | for (V source : sourcesToDelete) { | ||
135 | int count = tuplesForward.lookupOrEmpty(source).getCount(deleted); | ||
136 | for (int i=0; i< count; ++i) tuplesForward.removePair(source, deleted); | ||
137 | } | ||
138 | for (V target : targetsToDelete) { | ||
139 | int count = tuplesForward.lookupOrEmpty(deleted).getCount(target); | ||
140 | for (int i=0; i< count; ++i) tuplesForward.removePair(deleted, target); | ||
141 | } | ||
142 | |||
143 | if (tuplesBackward != null) { | ||
144 | for (V source : sourcesToDelete) { | ||
145 | int count = tuplesBackward.lookupOrEmpty(deleted).getCount(source); | ||
146 | for (int i=0; i< count; ++i) tuplesBackward.removePair(deleted, source); | ||
147 | } | ||
148 | for (V target : targetsToDelete) { | ||
149 | int count = tuplesBackward.lookupOrEmpty(target).getCount(deleted); | ||
150 | for (int i=0; i< count; ++i) tuplesBackward.removePair(target, deleted); | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | @Override | ||
156 | public String toString() { | ||
157 | StringBuilder sb = new StringBuilder("TcRelation = "); | ||
158 | |||
159 | for (V source : tuplesForward.distinctKeys()) { | ||
160 | IMemoryView<V> targets = tuplesForward.lookup(source); | ||
161 | for (V target : targets.distinctValues()) { | ||
162 | sb.append("{(" + source + "," + target + ")," + targets.getCount(target) + "} "); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | return sb.toString(); | ||
167 | } | ||
168 | |||
169 | @Override | ||
170 | public Set<V> getTupleEnds(V source) { | ||
171 | IMemoryView<V> tupEnds = tuplesForward.lookup(source); | ||
172 | if (tupEnds == null) | ||
173 | return null; | ||
174 | return tupEnds.distinctValues(); | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * Returns the set of nodes from which the target node is reachable, if already computed. | ||
179 | * | ||
180 | * @param target | ||
181 | * the target node | ||
182 | * @return the set of source nodes | ||
183 | * @throws UnsupportedOperationException if backwards index not computed | ||
184 | */ | ||
185 | public Set<V> getTupleStarts(V target) { | ||
186 | if (tuplesBackward != null) { | ||
187 | IMemoryView<V> tupStarts = tuplesBackward.lookup(target); | ||
188 | if (tupStarts == null) | ||
189 | return null; | ||
190 | return tupStarts.distinctValues(); | ||
191 | } else { | ||
192 | throw new UnsupportedOperationException("built without backward indexing"); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | @Override | ||
197 | public Set<V> getTupleStarts() { | ||
198 | Set<V> nodes = CollectionsFactory.createSet(); | ||
199 | for (V s : tuplesForward.distinctKeys()) { | ||
200 | nodes.add(s); | ||
201 | } | ||
202 | return nodes; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. | ||
207 | * | ||
208 | * @param source | ||
209 | * the source node | ||
210 | * @param target | ||
211 | * the target node | ||
212 | * @return true if tuple is present, false otherwise | ||
213 | */ | ||
214 | public boolean containsTuple(V source, V target) { | ||
215 | return tuplesForward.lookupOrEmpty(source).containsNonZero(target); | ||
216 | } | ||
217 | |||
218 | @SuppressWarnings("unchecked") | ||
219 | @Override | ||
220 | public boolean equals(Object obj) { | ||
221 | if (this == obj) { | ||
222 | return true; | ||
223 | } else if (obj == null || this.getClass() != obj.getClass()) { | ||
224 | return false; | ||
225 | } else { | ||
226 | CountingTcRelation<V> aTR = (CountingTcRelation<V>) obj; | ||
227 | |||
228 | return tuplesForward.equals(aTR.tuplesForward); | ||
229 | } | ||
230 | } | ||
231 | |||
232 | @Override | ||
233 | public int hashCode() { | ||
234 | return tuplesForward.hashCode(); | ||
235 | } | ||
236 | |||
237 | public static <V> CountingTcRelation<V> createFrom(IBiDirectionalGraphDataSource<V> gds) { | ||
238 | List<V> topologicalSorting = TopologicalSorting.compute(gds); | ||
239 | CountingTcRelation<V> tc = new CountingTcRelation<V>(true); | ||
240 | Collections.reverse(topologicalSorting); | ||
241 | for (V n : topologicalSorting) { | ||
242 | IMemoryView<V> sourceNodes = gds.getSourceNodes(n); | ||
243 | Set<V> tupEnds = tc.getTupleEnds(n); | ||
244 | for (V s : sourceNodes.distinctValues()) { | ||
245 | int count = sourceNodes.getCount(s); | ||
246 | for (int i = 0; i < count; i++) { | ||
247 | tc.updateTuple(s, n, true); | ||
248 | if (tupEnds != null) { | ||
249 | for (V t : tupEnds) { | ||
250 | tc.updateTuple(s, t, true); | ||
251 | } | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | } | ||
256 | |||
257 | return tc; | ||
258 | } | ||
259 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java new file mode 100644 index 00000000..b92d08bf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java | |||
@@ -0,0 +1,308 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.dred; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | import java.util.Set; | ||
19 | |||
20 | import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; | ||
21 | import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; | ||
22 | import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; | ||
23 | import tools.refinery.viatra.runtime.base.itc.alg.misc.dfs.DFSAlg; | ||
24 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
25 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
26 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; | ||
27 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; | ||
28 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
29 | |||
30 | /** | ||
31 | * This class is the optimized implementation of the DRED algorithm. | ||
32 | * | ||
33 | * @author Tamas Szabo | ||
34 | * | ||
35 | * @param <V> | ||
36 | * the type parameter of the nodes in the graph data source | ||
37 | */ | ||
38 | public class DRedAlg<V> implements IGraphObserver<V>, ITcDataSource<V> { | ||
39 | |||
40 | private IGraphDataSource<V> graphDataSource = null; | ||
41 | private DRedTcRelation<V> tc = null; | ||
42 | private DRedTcRelation<V> dtc = null; | ||
43 | private List<ITcObserver<V>> observers; | ||
44 | |||
45 | /** | ||
46 | * Constructs a new DRED algorithm and initializes the transitive closure relation with the given graph data source. | ||
47 | * Attach itself on the graph data source as an observer. | ||
48 | * | ||
49 | * @param gds | ||
50 | * the graph data source instance | ||
51 | */ | ||
52 | public DRedAlg(IGraphDataSource<V> gds) { | ||
53 | this.observers = new ArrayList<ITcObserver<V>>(); | ||
54 | this.graphDataSource = gds; | ||
55 | this.tc = new DRedTcRelation<V>(); | ||
56 | this.dtc = new DRedTcRelation<V>(); | ||
57 | initTc(); | ||
58 | graphDataSource.attachObserver(this); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Constructs a new DRED algorithm and initializes the transitive closure relation with the given relation. Attach | ||
63 | * itself on the graph data source as an observer. | ||
64 | * | ||
65 | * @param gds | ||
66 | * the graph data source instance | ||
67 | * @param tc | ||
68 | * the transitive closure instance | ||
69 | */ | ||
70 | public DRedAlg(IGraphDataSource<V> gds, DRedTcRelation<V> tc) { | ||
71 | this.graphDataSource = gds; | ||
72 | this.tc = tc; | ||
73 | this.dtc = new DRedTcRelation<V>(); | ||
74 | graphDataSource.attachObserver(this); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Initializes the transitive closure relation. | ||
79 | */ | ||
80 | private void initTc() { | ||
81 | DFSAlg<V> dfsa = new DFSAlg<V>(this.graphDataSource); | ||
82 | this.setTcRelation(dfsa.getTcRelation()); | ||
83 | this.graphDataSource.detachObserver(dfsa); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void edgeInserted(V source, V target) { | ||
88 | if (!source.equals(target)) { | ||
89 | Set<V> tupStarts = null; | ||
90 | Set<V> tupEnds = null; | ||
91 | Set<Tuple<V>> tuples = new HashSet<Tuple<V>>(); | ||
92 | |||
93 | if (!source.equals(target) && tc.addTuple(source, target)) { | ||
94 | tuples.add(new Tuple<V>(source, target)); | ||
95 | } | ||
96 | |||
97 | // 2. d+(tc(x,y)) :- d+(tc(x,z)) & lv(z,y) Descartes product | ||
98 | tupStarts = tc.getTupleStarts(source); | ||
99 | tupEnds = tc.getTupleEnds(target); | ||
100 | |||
101 | for (V s : tupStarts) { | ||
102 | for (V t : tupEnds) { | ||
103 | if (!s.equals(t) && tc.addTuple(s, t)) { | ||
104 | tuples.add(new Tuple<V>(s, t)); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | // (s, source) -> (source, target) | ||
110 | // tupStarts = tc.getTupleStarts(source); | ||
111 | for (V s : tupStarts) { | ||
112 | if (!s.equals(target) && tc.addTuple(s, target)) { | ||
113 | tuples.add(new Tuple<V>(s, target)); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | // (source, target) -> (target, t) | ||
118 | // tupEnds = tc.getTupleEnds(target); | ||
119 | for (V t : tupEnds) { | ||
120 | if (!source.equals(t) && tc.addTuple(source, t)) { | ||
121 | tuples.add(new Tuple<V>(source, t)); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | notifyTcObservers(tuples, 1); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public void edgeDeleted(V source, V target) { | ||
131 | if (!source.equals(target)) { | ||
132 | |||
133 | // Computing overestimate, Descartes product of A and B sets, where | ||
134 | // A: those nodes from which source is reachable | ||
135 | // B: those nodes which is reachable from target | ||
136 | |||
137 | Map<Tuple<V>, Integer> tuples = new HashMap<Tuple<V>, Integer>(); | ||
138 | Set<V> sources = tc.getTupleStarts(source); | ||
139 | Set<V> targets = tc.getTupleEnds(target); | ||
140 | |||
141 | tc.removeTuple(source, target); | ||
142 | tuples.put(new Tuple<V>(source, target), -1); | ||
143 | |||
144 | for (V s : sources) { | ||
145 | for (V t : targets) { | ||
146 | if (!s.equals(t)) { | ||
147 | tc.removeTuple(s, t); | ||
148 | tuples.put(new Tuple<V>(s, t), -1); | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | for (V s : sources) { | ||
154 | if (!s.equals(target)) { | ||
155 | tc.removeTuple(s, target); | ||
156 | tuples.put(new Tuple<V>(s, target), -1); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | for (V t : targets) { | ||
161 | if (!source.equals(t)) { | ||
162 | tc.removeTuple(source, t); | ||
163 | tuples.put(new Tuple<V>(source, t), -1); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | // System.out.println("overestimate: "+dtc); | ||
168 | |||
169 | // Modify overestimate with those tuples that have alternative derivations | ||
170 | // 1. q+(tc(x,y)) :- lv(x,y) | ||
171 | for (V s : graphDataSource.getAllNodes()) { | ||
172 | IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(s); | ||
173 | for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) { | ||
174 | for (int i = 0; i < entry.getValue(); i++) { | ||
175 | V t = entry.getKey(); | ||
176 | if (!s.equals(t)) { | ||
177 | tc.addTuple(s, t); | ||
178 | Tuple<V> tuple = new Tuple<V>(s, t); | ||
179 | Integer count = tuples.get(tuple); | ||
180 | if (count != null && count == -1) { | ||
181 | tuples.remove(tuple); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | // 2. q+(tc(x,y)) :- tcv(x,z) & lv(z,y) | ||
190 | DRedTcRelation<V> newTups = new DRedTcRelation<V>(); | ||
191 | dtc.clear(); | ||
192 | dtc.union(tc); | ||
193 | |||
194 | while (!dtc.isEmpty()) { | ||
195 | |||
196 | newTups.clear(); | ||
197 | newTups.union(dtc); | ||
198 | dtc.clear(); | ||
199 | |||
200 | for (V s : newTups.getTupleStarts()) { | ||
201 | for (V t : newTups.getTupleEnds(s)) { | ||
202 | IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(t); | ||
203 | if (targetNodes != null) { | ||
204 | for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) { | ||
205 | for (int i = 0; i < entry.getValue(); i++) { | ||
206 | V tn = entry.getKey(); | ||
207 | if (!s.equals(tn) && tc.addTuple(s, tn)) { | ||
208 | dtc.addTuple(s, tn); | ||
209 | tuples.remove(new Tuple<V>(s, tn)); | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | |||
218 | notifyTcObservers(tuples.keySet(), -1); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | @Override | ||
223 | public void nodeInserted(V n) { | ||
224 | // Node inserted does not result new tc tuple. | ||
225 | } | ||
226 | |||
227 | @Override | ||
228 | public void nodeDeleted(V n) { | ||
229 | // FIXME node deletion may involve the deletion of incoming and outgoing edges too | ||
230 | Set<V> set = tc.getTupleEnds(n); | ||
231 | Set<V> modSet = null; | ||
232 | |||
233 | // n -> target | ||
234 | modSet = new HashSet<V>(set); | ||
235 | |||
236 | for (V tn : modSet) { | ||
237 | this.tc.removeTuple(n, tn); | ||
238 | } | ||
239 | |||
240 | // source -> n | ||
241 | set = tc.getTupleStarts(n); | ||
242 | |||
243 | modSet = new HashSet<V>(set); | ||
244 | |||
245 | for (V sn : modSet) { | ||
246 | this.tc.removeTuple(sn, n); | ||
247 | } | ||
248 | } | ||
249 | |||
250 | public DRedTcRelation<V> getTcRelation() { | ||
251 | return this.tc; | ||
252 | } | ||
253 | |||
254 | public void setTcRelation(DRedTcRelation<V> tc) { | ||
255 | this.tc = tc; | ||
256 | } | ||
257 | |||
258 | @Override | ||
259 | public boolean isReachable(V source, V target) { | ||
260 | return tc.containsTuple(source, target); | ||
261 | } | ||
262 | |||
263 | @Override | ||
264 | public void attachObserver(ITcObserver<V> to) { | ||
265 | this.observers.add(to); | ||
266 | } | ||
267 | |||
268 | @Override | ||
269 | public void detachObserver(ITcObserver<V> to) { | ||
270 | this.observers.remove(to); | ||
271 | } | ||
272 | |||
273 | @Override | ||
274 | public Set<V> getAllReachableTargets(V source) { | ||
275 | return tc.getTupleEnds(source); | ||
276 | } | ||
277 | |||
278 | @Override | ||
279 | public Set<V> getAllReachableSources(V target) { | ||
280 | return tc.getTupleStarts(target); | ||
281 | } | ||
282 | |||
283 | protected void notifyTcObservers(Set<Tuple<V>> tuples, int dir) { | ||
284 | for (ITcObserver<V> o : observers) { | ||
285 | for (Tuple<V> t : tuples) { | ||
286 | if (!t.getSource().equals(t.getTarget())) { | ||
287 | if (dir == 1) { | ||
288 | o.tupleInserted(t.getSource(), t.getTarget()); | ||
289 | } | ||
290 | if (dir == -1) { | ||
291 | o.tupleDeleted(t.getSource(), t.getTarget()); | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | @Override | ||
299 | public void dispose() { | ||
300 | tc = null; | ||
301 | dtc = null; | ||
302 | } | ||
303 | |||
304 | @Override | ||
305 | public IGraphPathFinder<V> getPathFinder() { | ||
306 | return new DFSPathFinder<V>(graphDataSource, this); | ||
307 | } | ||
308 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java new file mode 100644 index 00000000..8543b79c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java | |||
@@ -0,0 +1,223 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.dred; | ||
11 | |||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Map.Entry; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; | ||
19 | |||
20 | public class DRedTcRelation<V> implements ITcRelation<V> { | ||
21 | |||
22 | // tc(a,b) means that b is transitively reachable from a | ||
23 | private Map<V, Set<V>> tuplesForward; | ||
24 | |||
25 | // data structure to efficiently get those nodes from which a given node is reachable | ||
26 | // symmetric to tuplesForward | ||
27 | private Map<V, Set<V>> tuplesBackward; | ||
28 | |||
29 | public DRedTcRelation() { | ||
30 | this.tuplesForward = new HashMap<V, Set<V>>(); | ||
31 | this.tuplesBackward = new HashMap<V, Set<V>>(); | ||
32 | } | ||
33 | |||
34 | public void clear() { | ||
35 | this.tuplesForward.clear(); | ||
36 | this.tuplesBackward.clear(); | ||
37 | } | ||
38 | |||
39 | public boolean isEmpty() { | ||
40 | return tuplesForward.isEmpty(); | ||
41 | } | ||
42 | |||
43 | public void removeTuple(V source, V target) { | ||
44 | |||
45 | // removing tuple from 'forward' tc relation | ||
46 | Set<V> sSet = tuplesForward.get(source); | ||
47 | if (sSet != null) { | ||
48 | sSet.remove(target); | ||
49 | if (sSet.size() == 0) | ||
50 | tuplesForward.remove(source); | ||
51 | } | ||
52 | |||
53 | // removing tuple from 'backward' tc relation | ||
54 | Set<V> tSet = tuplesBackward.get(target); | ||
55 | if (tSet != null) { | ||
56 | tSet.remove(source); | ||
57 | if (tSet.size() == 0) | ||
58 | tuplesBackward.remove(target); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false | ||
64 | * otherwise. | ||
65 | * | ||
66 | * @param source | ||
67 | * the source of the tuple | ||
68 | * @param target | ||
69 | * the target of the tuple | ||
70 | * @return true if the relation did not contain previously the tuple | ||
71 | */ | ||
72 | public boolean addTuple(V source, V target) { | ||
73 | |||
74 | // symmetric modification, it is sufficient to check the return value in one collection | ||
75 | // adding tuple to 'forward' tc relation | ||
76 | Set<V> sSet = tuplesForward.get(source); | ||
77 | if (sSet == null) { | ||
78 | Set<V> newSet = new HashSet<V>(); | ||
79 | newSet.add(target); | ||
80 | tuplesForward.put(source, newSet); | ||
81 | } else { | ||
82 | sSet.add(target); | ||
83 | } | ||
84 | |||
85 | // adding tuple to 'backward' tc relation | ||
86 | Set<V> tSet = tuplesBackward.get(target); | ||
87 | if (tSet == null) { | ||
88 | Set<V> newSet = new HashSet<V>(); | ||
89 | newSet.add(source); | ||
90 | tuplesBackward.put(target, newSet); | ||
91 | return true; | ||
92 | } else { | ||
93 | boolean ret = tSet.add(source); | ||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Union operation of two tc realtions. | ||
101 | * | ||
102 | * @param rA | ||
103 | * the other tc relation | ||
104 | */ | ||
105 | public void union(DRedTcRelation<V> rA) { | ||
106 | for (V source : rA.tuplesForward.keySet()) { | ||
107 | for (V target : rA.tuplesForward.get(source)) { | ||
108 | this.addTuple(source, target); | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Computes the difference of this tc relation and the given rA parameter. | ||
115 | * | ||
116 | * @param rA | ||
117 | * the subtrahend relation | ||
118 | */ | ||
119 | public void difference(DRedTcRelation<V> rA) { | ||
120 | for (V source : rA.tuplesForward.keySet()) { | ||
121 | for (V target : rA.tuplesForward.get(source)) { | ||
122 | this.removeTuple(source, target); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | |||
127 | @Override | ||
128 | public Set<V> getTupleEnds(V source) { | ||
129 | Set<V> t = tuplesForward.get(source); | ||
130 | return (t == null) ? new HashSet<V>() : new HashSet<V>(t); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Returns the set of nodes from which the target node is reachable. | ||
135 | * | ||
136 | * @param target | ||
137 | * the target node | ||
138 | * @return the set of source nodes | ||
139 | */ | ||
140 | public Set<V> getTupleStarts(V target) { | ||
141 | Set<V> t = tuplesBackward.get(target); | ||
142 | return (t == null) ? new HashSet<V>() : new HashSet<V>(t); | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public Set<V> getTupleStarts() { | ||
147 | Set<V> t = tuplesForward.keySet(); | ||
148 | return new HashSet<V>(t); | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public String toString() { | ||
153 | StringBuilder sb = new StringBuilder("TcRelation = "); | ||
154 | |||
155 | for (Entry<V, Set<V>> entry : this.tuplesForward.entrySet()) { | ||
156 | V source = entry.getKey(); | ||
157 | for (V target : entry.getValue()) { | ||
158 | sb.append("(" + source + "," + target + ") "); | ||
159 | } | ||
160 | } | ||
161 | return sb.toString(); | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. | ||
166 | * | ||
167 | * @param source | ||
168 | * the source node | ||
169 | * @param target | ||
170 | * the target node | ||
171 | * @return true if tuple is present, false otherwise | ||
172 | */ | ||
173 | public boolean containsTuple(V source, V target) { | ||
174 | if (tuplesForward.containsKey(source)) { | ||
175 | if (tuplesForward.get(source).contains(target)) | ||
176 | return true; | ||
177 | } | ||
178 | return false; | ||
179 | } | ||
180 | |||
181 | @SuppressWarnings("unchecked") | ||
182 | @Override | ||
183 | public boolean equals(Object obj) { | ||
184 | if (this == obj) { | ||
185 | return true; | ||
186 | } | ||
187 | if ((obj == null) || (obj.getClass() != this.getClass())) { | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | DRedTcRelation<V> aTR = (DRedTcRelation<V>) obj; | ||
192 | |||
193 | for (Entry<V, Set<V>> entry : aTR.tuplesForward.entrySet()) { | ||
194 | V source = entry.getKey(); | ||
195 | for (V target : entry.getValue()) { | ||
196 | if (!this.containsTuple(source, target)) | ||
197 | return false; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | for (Entry<V, Set<V>> entry : this.tuplesForward.entrySet()) { | ||
202 | V source = entry.getKey(); | ||
203 | for (V target : entry.getValue()) { | ||
204 | if (!aTR.containsTuple(source, target)) | ||
205 | return false; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | return true; | ||
210 | } | ||
211 | |||
212 | @Override | ||
213 | public int hashCode() { | ||
214 | int hash = 7; | ||
215 | hash = 31 * hash + tuplesForward.hashCode(); | ||
216 | hash = 31 * hash + tuplesBackward.hashCode(); | ||
217 | return hash; | ||
218 | } | ||
219 | |||
220 | public Map<V, Set<V>> getTuplesForward() { | ||
221 | return tuplesForward; | ||
222 | } | ||
223 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java new file mode 100644 index 00000000..8b8908b1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java | |||
@@ -0,0 +1,110 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.fw; | ||
11 | |||
12 | import java.util.HashMap; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; | ||
16 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
17 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
18 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
19 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
21 | |||
22 | public class FloydWarshallAlg<V> implements IGraphObserver<V> { | ||
23 | |||
24 | private DRedTcRelation<V> tc = null; | ||
25 | private IBiDirectionalGraphDataSource<V> gds = null; | ||
26 | |||
27 | public FloydWarshallAlg(IGraphDataSource<V> gds) { | ||
28 | if (gds instanceof IBiDirectionalGraphDataSource<?>) { | ||
29 | this.gds = (IBiDirectionalGraphDataSource<V>) gds; | ||
30 | } else { | ||
31 | this.gds = new IBiDirectionalWrapper<V>(gds); | ||
32 | } | ||
33 | |||
34 | this.tc = new DRedTcRelation<V>(); | ||
35 | gds.attachObserver(this); | ||
36 | generateTc(); | ||
37 | } | ||
38 | |||
39 | private void generateTc() { | ||
40 | |||
41 | tc.clear(); | ||
42 | |||
43 | int n = gds.getAllNodes().size(); | ||
44 | Map<V, Integer> mapForw = new HashMap<V, Integer>(); | ||
45 | Map<Integer, V> mapBackw = new HashMap<Integer, V>(); | ||
46 | int[][] P = new int[n][n]; | ||
47 | |||
48 | int i, j, k; | ||
49 | |||
50 | // initialize adjacent matrix | ||
51 | for (i = 0; i < n; i++) { | ||
52 | for (j = 0; j < n; j++) { | ||
53 | P[i][j] = 0; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | i = 0; | ||
58 | for (V node : gds.getAllNodes()) { | ||
59 | mapForw.put(node, i); | ||
60 | mapBackw.put(i, node); | ||
61 | i++; | ||
62 | } | ||
63 | |||
64 | for (V source : gds.getAllNodes()) { | ||
65 | IMemoryView<V> targets = gds.getTargetNodes(source); | ||
66 | for (V target : targets.distinctValues()) { | ||
67 | P[mapForw.get(source)][mapForw.get(target)] = 1; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | for (k = 0; k < n; k++) { | ||
72 | for (i = 0; i < n; i++) { | ||
73 | for (j = 0; j < n; j++) { | ||
74 | P[i][j] = P[i][j] | (P[i][k] & P[k][j]); | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | for (i = 0; i < n; i++) { | ||
80 | for (j = 0; j < n; j++) { | ||
81 | if (P[i][j] == 1 && i != j) | ||
82 | tc.addTuple(mapBackw.get(i), mapBackw.get(j)); | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public void edgeInserted(V source, V target) { | ||
89 | generateTc(); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public void edgeDeleted(V source, V target) { | ||
94 | generateTc(); | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public void nodeInserted(V n) { | ||
99 | generateTc(); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public void nodeDeleted(V n) { | ||
104 | generateTc(); | ||
105 | } | ||
106 | |||
107 | public DRedTcRelation<V> getTcRelation() { | ||
108 | return this.tc; | ||
109 | } | ||
110 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java new file mode 100644 index 00000000..fdf64f77 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.incscc; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
13 | |||
14 | /** | ||
15 | * @author Tamas Szabo | ||
16 | * | ||
17 | */ | ||
18 | public class CountingListener<V> implements ITcObserver<V> { | ||
19 | |||
20 | private IncSCCAlg<V> alg; | ||
21 | |||
22 | public CountingListener(IncSCCAlg<V> alg) { | ||
23 | this.alg = alg; | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public void tupleInserted(V source, V target) { | ||
28 | alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.INSERT); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public void tupleDeleted(V source, V target) { | ||
33 | alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.DELETE); | ||
34 | } | ||
35 | |||
36 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java new file mode 100644 index 00000000..5d720e75 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java | |||
@@ -0,0 +1,645 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.incscc; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Iterator; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | import java.util.Objects; | ||
19 | import java.util.Set; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.base.itc.alg.counting.CountingAlg; | ||
22 | import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; | ||
23 | import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; | ||
24 | import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; | ||
25 | import tools.refinery.viatra.runtime.base.itc.alg.util.CollectionHelper; | ||
26 | import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; | ||
27 | import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; | ||
28 | import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; | ||
29 | import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; | ||
30 | import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; | ||
31 | import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; | ||
32 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
33 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; | ||
34 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
35 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
36 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; | ||
37 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; | ||
38 | import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind; | ||
39 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
40 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
41 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
42 | |||
43 | /** | ||
44 | * Incremental SCC maintenance + counting algorithm. | ||
45 | * | ||
46 | * @author Tamas Szabo | ||
47 | * | ||
48 | * @param <V> | ||
49 | * the type parameter of the nodes in the graph data source | ||
50 | */ | ||
51 | public class IncSCCAlg<V> implements IGraphObserver<V>, ITcDataSource<V> { | ||
52 | |||
53 | public UnionFind<V> sccs; | ||
54 | public IBiDirectionalGraphDataSource<V> gds; | ||
55 | private CountingAlg<V> counting; | ||
56 | private Graph<V> reducedGraph; | ||
57 | private IBiDirectionalGraphDataSource<V> reducedGraphIndexer; | ||
58 | private List<ITcObserver<V>> observers; | ||
59 | private CountingListener<V> countingListener; | ||
60 | |||
61 | public IncSCCAlg(IGraphDataSource<V> graphDataSource) { | ||
62 | |||
63 | if (graphDataSource instanceof IBiDirectionalGraphDataSource<?>) { | ||
64 | gds = (IBiDirectionalGraphDataSource<V>) graphDataSource; | ||
65 | } else { | ||
66 | gds = new IBiDirectionalWrapper<V>(graphDataSource); | ||
67 | } | ||
68 | observers = CollectionsFactory.createObserverList(); | ||
69 | sccs = new UnionFind<V>(); | ||
70 | reducedGraph = new Graph<V>(); | ||
71 | reducedGraphIndexer = new IBiDirectionalWrapper<V>(reducedGraph); | ||
72 | countingListener = new CountingListener<V>(this); | ||
73 | initalizeInternalDataStructures(); | ||
74 | gds.attachObserver(this); | ||
75 | } | ||
76 | |||
77 | private void initalizeInternalDataStructures() { | ||
78 | SCCResult<V> _sccres = SCC.computeSCC(gds); | ||
79 | Set<Set<V>> _sccs = _sccres.getSccs(); | ||
80 | |||
81 | for (Set<V> _set : _sccs) { | ||
82 | sccs.makeSet(_set); | ||
83 | } | ||
84 | |||
85 | // Initalization of the reduced graph | ||
86 | for (V n : sccs.getPartitionHeads()) { | ||
87 | reducedGraph.insertNode(n); | ||
88 | } | ||
89 | |||
90 | for (V source : gds.getAllNodes()) { | ||
91 | final IMemoryView<V> targetNodes = gds.getTargetNodes(source); | ||
92 | for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) { | ||
93 | for (int i = 0; i < entry.getValue(); i++) { | ||
94 | V target = entry.getKey(); | ||
95 | V sourceRoot = sccs.find(source); | ||
96 | V targetRoot = sccs.find(target); | ||
97 | |||
98 | if (!sourceRoot.equals(targetRoot)) { | ||
99 | reducedGraph.insertEdge(sourceRoot, targetRoot); | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | counting = new CountingAlg<V>(reducedGraph); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public void edgeInserted(V source, V target) { | ||
110 | V sourceRoot = sccs.find(source); | ||
111 | V targetRoot = sccs.find(target); | ||
112 | |||
113 | // Different SCC | ||
114 | if (!sourceRoot.equals(targetRoot)) { | ||
115 | |||
116 | // source is reachable from target? | ||
117 | if (counting.isReachable(targetRoot, sourceRoot)) { | ||
118 | |||
119 | Set<V> predecessorRoots = counting.getAllReachableSources(sourceRoot); | ||
120 | Set<V> successorRoots = counting.getAllReachableTargets(targetRoot); | ||
121 | |||
122 | // 1. intersection of source and target roots, these will be in the merged SCC | ||
123 | Set<V> isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots); | ||
124 | isectRoots.add(sourceRoot); | ||
125 | isectRoots.add(targetRoot); | ||
126 | |||
127 | // notifications must be issued before Union-Find modifications | ||
128 | if (observers.size() > 0) { | ||
129 | Set<V> sourceSCCs = createSetNullTolerant(predecessorRoots); | ||
130 | sourceSCCs.add(sourceRoot); | ||
131 | Set<V> targetSCCs = createSetNullTolerant(successorRoots); | ||
132 | targetSCCs.add(targetRoot); | ||
133 | |||
134 | // tracing back to actual nodes | ||
135 | for (V sourceSCC : sourceSCCs) { | ||
136 | targetLoop: for (V targetSCC : targetSCCs) { | ||
137 | if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; | ||
138 | |||
139 | boolean needsNotification = | ||
140 | // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. | ||
141 | // Issue notifications only if there is no self-loop present at the moment | ||
142 | (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper | ||
143 | .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) | ||
144 | || | ||
145 | // Case 2. sourceSCC and targetSCC are different sccs. | ||
146 | (!sourceSCC.equals(targetSCC)); | ||
147 | // if self loop is already present omit the notification | ||
148 | if (needsNotification) { | ||
149 | notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), | ||
150 | Direction.INSERT); | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | // 2. delete edges, nodes | ||
157 | List<V> sourceSCCs = new ArrayList<V>(); | ||
158 | List<V> targetSCCs = new ArrayList<V>(); | ||
159 | |||
160 | for (V r : isectRoots) { | ||
161 | List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(r); | ||
162 | List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(r); | ||
163 | |||
164 | for (V sourceSCC : sourceSCCsOfSCC) { | ||
165 | if (!sourceSCC.equals(r)) { | ||
166 | reducedGraph.deleteEdgeIfExists(sourceSCC, r); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | for (V targetSCC : targetSCCsOfSCC) { | ||
171 | if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) { | ||
172 | reducedGraph.deleteEdgeIfExists(r, targetSCC); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | sourceSCCs.addAll(sourceSCCsOfSCC); | ||
177 | targetSCCs.addAll(targetSCCsOfSCC); | ||
178 | } | ||
179 | |||
180 | for (V r : isectRoots) { | ||
181 | reducedGraph.deleteNode(r); | ||
182 | } | ||
183 | |||
184 | // 3. union | ||
185 | Iterator<V> iterator = isectRoots.iterator(); | ||
186 | V newRoot = iterator.next(); | ||
187 | while (iterator.hasNext()) { | ||
188 | newRoot = sccs.union(newRoot, iterator.next()); | ||
189 | } | ||
190 | |||
191 | // 4. add new node | ||
192 | reducedGraph.insertNode(newRoot); | ||
193 | |||
194 | // 5. add edges | ||
195 | Set<V> containedNodes = sccs.getPartition(newRoot); | ||
196 | |||
197 | for (V sourceSCC : sourceSCCs) { | ||
198 | if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) { | ||
199 | reducedGraph.insertEdge(sourceSCC, newRoot); | ||
200 | } | ||
201 | } | ||
202 | for (V targetSCC : targetSCCs) { | ||
203 | if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) { | ||
204 | reducedGraph.insertEdge(newRoot, targetSCC); | ||
205 | } | ||
206 | } | ||
207 | } else { | ||
208 | if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) { | ||
209 | counting.attachObserver(countingListener); | ||
210 | } | ||
211 | reducedGraph.insertEdge(sourceRoot, targetRoot); | ||
212 | counting.detachObserver(countingListener); | ||
213 | } | ||
214 | } else { | ||
215 | // Notifications about self-loops | ||
216 | if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 | ||
217 | && GraphHelper.getEdgeCount(source, target, gds) == 1) { | ||
218 | notifyTcObservers(source, source, Direction.INSERT); | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | @Override | ||
224 | public void edgeDeleted(V source, V target) { | ||
225 | V sourceRoot = sccs.find(source); | ||
226 | V targetRoot = sccs.find(target); | ||
227 | |||
228 | if (!sourceRoot.equals(targetRoot)) { | ||
229 | if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) { | ||
230 | counting.attachObserver(countingListener); | ||
231 | } | ||
232 | reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot); | ||
233 | counting.detachObserver(countingListener); | ||
234 | } else { | ||
235 | // get the graph for the scc whose root is sourceRoot | ||
236 | Graph<V> g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds); | ||
237 | |||
238 | // if source is not reachable from target anymore | ||
239 | if (!BFS.isReachable(source, target, g)) { | ||
240 | // create copies of the current state before destructive manipulation | ||
241 | Map<V, Integer> reachableSources = CollectionsFactory.createMap(); | ||
242 | for (Entry<V, Integer> entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) { | ||
243 | reachableSources.put(entry.getKey(), entry.getValue()); | ||
244 | } | ||
245 | Map<V, Integer> reachableTargets = CollectionsFactory.createMap(); | ||
246 | for (Entry<V, Integer> entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) { | ||
247 | reachableTargets.put(entry.getKey(), entry.getValue()); | ||
248 | } | ||
249 | |||
250 | SCCResult<V> _newSccs = SCC.computeSCC(g); | ||
251 | |||
252 | // delete scc node (and with its edges too) | ||
253 | for (Entry<V, Integer> entry : reachableSources.entrySet()) { | ||
254 | V s = entry.getKey(); | ||
255 | for (int i = 0; i < entry.getValue(); i++) { | ||
256 | reducedGraph.deleteEdgeIfExists(s, sourceRoot); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | for (Entry<V, Integer> entry : reachableTargets.entrySet()) { | ||
261 | V t = entry.getKey(); | ||
262 | for (int i = 0; i < entry.getValue(); i++) { | ||
263 | reducedGraph.deleteEdgeIfExists(sourceRoot, t); | ||
264 | } | ||
265 | } | ||
266 | |||
267 | sccs.deleteSet(sourceRoot); | ||
268 | reducedGraph.deleteNode(sourceRoot); | ||
269 | |||
270 | Set<Set<V>> newSCCs = _newSccs.getSccs(); | ||
271 | Set<V> newSCCRoots = CollectionsFactory.createSet(); | ||
272 | |||
273 | // add new nodes and edges to the reduced graph | ||
274 | for (Set<V> newSCC : newSCCs) { | ||
275 | V newRoot = sccs.makeSet(newSCC); | ||
276 | reducedGraph.insertNode(newRoot); | ||
277 | newSCCRoots.add(newRoot); | ||
278 | } | ||
279 | for (V newSCCRoot : newSCCRoots) { | ||
280 | List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot); | ||
281 | List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot); | ||
282 | |||
283 | for (V sourceSCC : sourceSCCsOfSCC) { | ||
284 | if (!sourceSCC.equals(newSCCRoot)) { | ||
285 | reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot); | ||
286 | } | ||
287 | } | ||
288 | for (V targetSCC : targetSCCsOfSCC) { | ||
289 | if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot)) | ||
290 | reducedGraph.insertEdge(newSCCRoot, targetSCC); | ||
291 | } | ||
292 | } | ||
293 | |||
294 | // Must be after the union-find modifications | ||
295 | if (observers.size() > 0) { | ||
296 | V newSourceRoot = sccs.find(source); | ||
297 | V newTargetRoot = sccs.find(target); | ||
298 | |||
299 | Set<V> sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot)); | ||
300 | sourceSCCs.add(newSourceRoot); | ||
301 | |||
302 | Set<V> targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot)); | ||
303 | targetSCCs.add(newTargetRoot); | ||
304 | |||
305 | for (V sourceSCC : sourceSCCs) { | ||
306 | targetLoop: for (V targetSCC : targetSCCs) { | ||
307 | if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; | ||
308 | |||
309 | boolean needsNotification = | ||
310 | // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. | ||
311 | // Issue notifications only if there is no self-loop present at the moment | ||
312 | (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper | ||
313 | .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) | ||
314 | || | ||
315 | // Case 2. sourceSCC and targetSCC are different sccs. | ||
316 | (!sourceSCC.equals(targetSCC)); | ||
317 | // if self loop is already present omit the notification | ||
318 | if (needsNotification) { | ||
319 | notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), | ||
320 | Direction.DELETE); | ||
321 | } | ||
322 | } | ||
323 | } | ||
324 | } | ||
325 | } else { | ||
326 | // only handle self-loop notifications - sourceRoot equals to targetRoot | ||
327 | if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 | ||
328 | && GraphHelper.getEdgeCount(source, target, gds) == 0) { | ||
329 | notifyTcObservers(source, source, Direction.DELETE); | ||
330 | } | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
335 | @Override | ||
336 | public void nodeInserted(V n) { | ||
337 | sccs.makeSet(n); | ||
338 | reducedGraph.insertNode(n); | ||
339 | } | ||
340 | |||
341 | @Override | ||
342 | public void nodeDeleted(V n) { | ||
343 | IMemoryView<V> sources = gds.getSourceNodes(n); | ||
344 | IMemoryView<V> targets = gds.getTargetNodes(n); | ||
345 | |||
346 | for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) { | ||
347 | for (int i = 0; i < entry.getValue(); i++) { | ||
348 | V source = entry.getKey(); | ||
349 | edgeDeleted(source, n); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | for (Entry<V, Integer> entry : targets.entriesWithMultiplicities()) { | ||
354 | for (int i = 0; i < entry.getValue(); i++) { | ||
355 | V target = entry.getKey(); | ||
356 | edgeDeleted(n, target); | ||
357 | } | ||
358 | } | ||
359 | |||
360 | sccs.deleteSet(n); | ||
361 | } | ||
362 | |||
363 | @Override | ||
364 | public void attachObserver(ITcObserver<V> to) { | ||
365 | observers.add(to); | ||
366 | } | ||
367 | |||
368 | @Override | ||
369 | public void detachObserver(ITcObserver<V> to) { | ||
370 | observers.remove(to); | ||
371 | } | ||
372 | |||
373 | @Override | ||
374 | public Set<V> getAllReachableTargets(V source) { | ||
375 | V sourceRoot = sccs.find(source); | ||
376 | Set<V> containedNodes = sccs.getPartition(sourceRoot); | ||
377 | Set<V> targets = CollectionsFactory.createSet(); | ||
378 | |||
379 | if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) { | ||
380 | targets.addAll(containedNodes); | ||
381 | } | ||
382 | |||
383 | Set<V> rootSet = counting.getAllReachableTargets(sourceRoot); | ||
384 | if (rootSet != null) { | ||
385 | for (V _root : rootSet) { | ||
386 | targets.addAll(sccs.getPartition(_root)); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | return targets; | ||
391 | } | ||
392 | |||
393 | @Override | ||
394 | public Set<V> getAllReachableSources(V target) { | ||
395 | V targetRoot = sccs.find(target); | ||
396 | Set<V> containedNodes = sccs.getPartition(targetRoot); | ||
397 | Set<V> sources = CollectionsFactory.createSet(); | ||
398 | |||
399 | if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) { | ||
400 | sources.addAll(containedNodes); | ||
401 | } | ||
402 | |||
403 | Set<V> rootSet = counting.getAllReachableSources(targetRoot); | ||
404 | if (rootSet != null) { | ||
405 | for (V _root : rootSet) { | ||
406 | sources.addAll(sccs.getPartition(_root)); | ||
407 | } | ||
408 | } | ||
409 | return sources; | ||
410 | } | ||
411 | |||
412 | @Override | ||
413 | public boolean isReachable(V source, V target) { | ||
414 | V sourceRoot = sccs.find(source); | ||
415 | V targetRoot = sccs.find(target); | ||
416 | |||
417 | if (sourceRoot.equals(targetRoot)) | ||
418 | return true; | ||
419 | else | ||
420 | return counting.isReachable(sourceRoot, targetRoot); | ||
421 | } | ||
422 | |||
423 | public List<V> getReachabilityPath(V source, V target) { | ||
424 | if (!isReachable(source, target)) { | ||
425 | return null; | ||
426 | } else { | ||
427 | Set<V> sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source), | ||
428 | counting.getAllReachableSources(target)); | ||
429 | sccsInSubGraph.add(sccs.find(source)); | ||
430 | sccsInSubGraph.add(sccs.find(target)); | ||
431 | Set<V> nodesInSubGraph = CollectionsFactory.createSet(); | ||
432 | |||
433 | for (V sccRoot : sccsInSubGraph) { | ||
434 | nodesInSubGraph.addAll(sccs.getPartition(sccRoot)); | ||
435 | } | ||
436 | |||
437 | return GraphHelper.constructPath(source, target, nodesInSubGraph, gds); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | // for JUnit | ||
442 | public boolean checkTcRelation(DRedTcRelation<V> tc) { | ||
443 | |||
444 | for (V s : tc.getTupleStarts()) { | ||
445 | for (V t : tc.getTupleEnds(s)) { | ||
446 | if (!isReachable(s, t)) | ||
447 | return false; | ||
448 | } | ||
449 | } | ||
450 | |||
451 | for (V root : counting.getTcRelation().getTupleStarts()) { | ||
452 | for (V end : counting.getTcRelation().getTupleEnds(root)) { | ||
453 | for (V s : sccs.getPartition(root)) { | ||
454 | for (V t : sccs.getPartition(end)) { | ||
455 | if (!tc.containsTuple(s, t)) | ||
456 | return false; | ||
457 | } | ||
458 | } | ||
459 | } | ||
460 | } | ||
461 | |||
462 | return true; | ||
463 | } | ||
464 | |||
465 | /** | ||
466 | * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present | ||
467 | * multiple times in the returned list (multiple edges between the two SCCs). | ||
468 | * | ||
469 | * @param root | ||
470 | * @return the list of reachable target SCCs | ||
471 | */ | ||
472 | private List<V> getSourceSCCsOfSCC(V root) { | ||
473 | List<V> sourceSCCs = new ArrayList<V>(); | ||
474 | |||
475 | for (V containedNode : this.sccs.getPartition(root)) { | ||
476 | IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode); | ||
477 | for (V source : sourceNodes.distinctValues()) { | ||
478 | sourceSCCs.add(this.sccs.find(source)); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | return sourceSCCs; | ||
483 | } | ||
484 | |||
485 | /** | ||
486 | * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph, | ||
487 | * false otherwise (if this SCC is a source in the reduced graph). | ||
488 | * | ||
489 | * @param root the root node of an SCC | ||
490 | * @return true if it has incoming edges, false otherwise | ||
491 | * @since 1.6 | ||
492 | */ | ||
493 | public boolean hasIncomingEdges(final V root) { | ||
494 | for (final V containedNode : this.sccs.getPartition(root)) { | ||
495 | final IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode); | ||
496 | for (final V source : sourceNodes.distinctValues()) { | ||
497 | final V otherRoot = this.sccs.find(source); | ||
498 | if (!Objects.equals(root, otherRoot)) { | ||
499 | return true; | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | return false; | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present | ||
508 | * multiple times in the returned list (multiple edges between the two SCCs). | ||
509 | * | ||
510 | * @param root | ||
511 | * @return the list of reachable target SCCs | ||
512 | */ | ||
513 | private List<V> getTargetSCCsOfSCC(V root) { | ||
514 | List<V> targetSCCs = new ArrayList<V>(); | ||
515 | |||
516 | for (V containedNode : this.sccs.getPartition(root)) { | ||
517 | IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode); | ||
518 | for (V target : targetNodes.distinctValues()) { | ||
519 | targetSCCs.add(this.sccs.find(target)); | ||
520 | } | ||
521 | } | ||
522 | |||
523 | return targetSCCs; | ||
524 | } | ||
525 | |||
526 | /** | ||
527 | * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph, | ||
528 | * false otherwise (if this SCC is a sink in the reduced graph). | ||
529 | * | ||
530 | * @param root the root node of an SCC | ||
531 | * @return true if it has outgoing edges, false otherwise | ||
532 | * @since 1.6 | ||
533 | */ | ||
534 | public boolean hasOutgoingEdges(V root) { | ||
535 | for (final V containedNode : this.sccs.getPartition(root)) { | ||
536 | final IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode); | ||
537 | for (final V target : targetNodes.distinctValues()) { | ||
538 | final V otherRoot = this.sccs.find(target); | ||
539 | if (!Objects.equals(root, otherRoot)) { | ||
540 | return true; | ||
541 | } | ||
542 | } | ||
543 | } | ||
544 | return false; | ||
545 | } | ||
546 | |||
547 | @Override | ||
548 | public void dispose() { | ||
549 | gds.detachObserver(this); | ||
550 | counting.dispose(); | ||
551 | } | ||
552 | |||
553 | /** | ||
554 | * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification | ||
555 | * will be the Descartes product of the two sets given. | ||
556 | * | ||
557 | * @param sources | ||
558 | * the source nodes | ||
559 | * @param targets | ||
560 | * the target nodes | ||
561 | * @param direction | ||
562 | */ | ||
563 | protected void notifyTcObservers(Set<V> sources, Set<V> targets, Direction direction) { | ||
564 | for (V s : sources) { | ||
565 | for (V t : targets) { | ||
566 | notifyTcObservers(s, t, direction); | ||
567 | } | ||
568 | } | ||
569 | } | ||
570 | |||
571 | private void notifyTcObservers(V source, V target, Direction direction) { | ||
572 | for (ITcObserver<V> observer : observers) { | ||
573 | if (direction == Direction.INSERT) { | ||
574 | observer.tupleInserted(source, target); | ||
575 | } | ||
576 | if (direction == Direction.DELETE) { | ||
577 | observer.tupleDeleted(source, target); | ||
578 | } | ||
579 | } | ||
580 | } | ||
581 | |||
582 | /** | ||
583 | * Returns the node that is selected as the representative of the SCC containing the argument. | ||
584 | * @since 1.6 | ||
585 | */ | ||
586 | public V getRepresentative(V node) { | ||
587 | return sccs.find(node); | ||
588 | } | ||
589 | |||
590 | public Set<Tuple<V>> getTcRelation() { | ||
591 | Set<Tuple<V>> resultSet = new HashSet<Tuple<V>>(); | ||
592 | |||
593 | for (V sourceRoot : sccs.getPartitionHeads()) { | ||
594 | Set<V> sources = sccs.getPartition(sourceRoot); | ||
595 | if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) { | ||
596 | for (V source : sources) { | ||
597 | for (V target : sources) { | ||
598 | resultSet.add(new Tuple<V>(source, target)); | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | |||
603 | Set<V> reachableTargets = counting.getAllReachableTargets(sourceRoot); | ||
604 | if (reachableTargets != null) { | ||
605 | for (V targetRoot : reachableTargets) { | ||
606 | for (V source : sources) { | ||
607 | for (V target : sccs.getPartition(targetRoot)) { | ||
608 | resultSet.add(new Tuple<V>(source, target)); | ||
609 | } | ||
610 | } | ||
611 | } | ||
612 | } | ||
613 | } | ||
614 | |||
615 | return resultSet; | ||
616 | } | ||
617 | |||
618 | public boolean isIsolated(V node) { | ||
619 | IMemoryView<V> targets = gds.getTargetNodes(node); | ||
620 | IMemoryView<V> sources = gds.getSourceNodes(node); | ||
621 | return targets.isEmpty() && sources.isEmpty(); | ||
622 | } | ||
623 | |||
624 | @Override | ||
625 | public IGraphPathFinder<V> getPathFinder() { | ||
626 | return new DFSPathFinder<V>(gds, this); | ||
627 | } | ||
628 | |||
629 | /** | ||
630 | * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)}) | ||
631 | * @since 1.6 | ||
632 | */ | ||
633 | public Graph<V> getReducedGraph() { | ||
634 | return reducedGraph; | ||
635 | } | ||
636 | |||
637 | private static <V> Set<V> createSetNullTolerant(Set<V> initial) { | ||
638 | if (initial != null) | ||
639 | return CollectionsFactory.createSet(initial); | ||
640 | else | ||
641 | return CollectionsFactory.createSet(); | ||
642 | } | ||
643 | |||
644 | |||
645 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java new file mode 100644 index 00000000..51017b1a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java | |||
@@ -0,0 +1,146 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Abel Hegedus and IncQueryLabs Ltd. | ||
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.base.itc.alg.misc; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Deque; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Iterator; | ||
15 | import java.util.LinkedList; | ||
16 | import java.util.List; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
20 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | |||
23 | /** | ||
24 | * A depth-first search implementation of the {@link IGraphPathFinder}. | ||
25 | * | ||
26 | * TODO use ITC to filter nodes that must be traversed, instead of checks | ||
27 | * | ||
28 | * @author Abel Hegedus | ||
29 | * | ||
30 | * @param <V> | ||
31 | * the node type of the graph | ||
32 | */ | ||
33 | public class DFSPathFinder<V> implements IGraphPathFinder<V> { | ||
34 | |||
35 | private IGraphDataSource<V> graph; | ||
36 | private ITcDataSource<V> itc; | ||
37 | |||
38 | public DFSPathFinder(IGraphDataSource<V> graph, ITcDataSource<V> itc) { | ||
39 | this.graph = graph; | ||
40 | this.itc = itc; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public Iterable<Deque<V>> getAllPaths(V sourceNode, V targetNode) { | ||
45 | Set<V> endNodes = new HashSet<V>(); | ||
46 | endNodes.add(targetNode); | ||
47 | return getAllPathsToTargets(sourceNode, endNodes); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public Iterable<Deque<V>> getAllPathsToTargets(V sourceNode, Set<V> targetNodes) { | ||
52 | List<Deque<V>> paths = new ArrayList<Deque<V>>(); | ||
53 | Deque<V> visited = new LinkedList<V>(); | ||
54 | Set<V> reachableTargets = new HashSet<V>(); | ||
55 | for (V targetNode : targetNodes) { | ||
56 | if (itc.isReachable(sourceNode, targetNode)) { | ||
57 | reachableTargets.add(targetNode); | ||
58 | } | ||
59 | } | ||
60 | if (!reachableTargets.isEmpty()) { | ||
61 | return paths; | ||
62 | } | ||
63 | visited.add(sourceNode); | ||
64 | return getPaths(paths, visited, reachableTargets); | ||
65 | } | ||
66 | |||
67 | protected Iterable<Deque<V>> getPaths(List<Deque<V>> paths, Deque<V> visited, Set<V> targetNodes) { | ||
68 | IMemoryView<V> nodes = graph.getTargetNodes(visited.getLast()); | ||
69 | // examine adjacent nodes | ||
70 | for (V node : nodes.distinctValues()) { | ||
71 | if (visited.contains(node)) { | ||
72 | continue; | ||
73 | } | ||
74 | if (targetNodes.contains(node)) { | ||
75 | visited.add(node); | ||
76 | // clone visited LinkedList | ||
77 | Deque<V> visitedClone = new LinkedList<V>(visited); | ||
78 | paths.add(visitedClone); | ||
79 | visited.removeLast(); | ||
80 | break; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | // in breadth-first, recursion needs to come after visiting connected nodes | ||
85 | for (V node : nodes.distinctValues()) { | ||
86 | if (visited.contains(node) || targetNodes.contains(node)) { | ||
87 | continue; | ||
88 | } | ||
89 | boolean canReachTarget = false; | ||
90 | for (V target : targetNodes) { | ||
91 | if (itc.isReachable(node, target)) { | ||
92 | canReachTarget = true; | ||
93 | break; | ||
94 | } | ||
95 | } | ||
96 | if (canReachTarget) { | ||
97 | visited.addLast(node); | ||
98 | getPaths(paths, visited, targetNodes); | ||
99 | visited.removeLast(); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | return paths; | ||
104 | } | ||
105 | |||
106 | public String printPaths(List<Deque<V>> paths) { | ||
107 | StringBuilder sb = new StringBuilder(); | ||
108 | for (Deque<V> visited : paths) { | ||
109 | sb.append("Path: "); | ||
110 | for (V node : visited) { | ||
111 | sb.append(node); | ||
112 | sb.append(" --> "); | ||
113 | } | ||
114 | sb.append("\n"); | ||
115 | } | ||
116 | return sb.toString(); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public Deque<V> getPath(V sourceNode, V targetNode) { | ||
121 | // TODO optimize | ||
122 | Iterable<Deque<V>> allPaths = getAllPaths(sourceNode, targetNode); | ||
123 | Iterator<Deque<V>> pathIterator = allPaths.iterator(); | ||
124 | return pathIterator.hasNext() ? pathIterator.next() : new LinkedList<V>(); | ||
125 | } | ||
126 | |||
127 | @Override | ||
128 | public Iterable<Deque<V>> getShortestPaths(V sourceNode, V targetNode) { | ||
129 | // TODO optimize | ||
130 | Iterable<Deque<V>> allPaths = getAllPaths(sourceNode, targetNode); | ||
131 | List<Deque<V>> shortestPaths = new ArrayList<Deque<V>>(); | ||
132 | int shortestPathLength = -1; | ||
133 | for (Deque<V> path : allPaths) { | ||
134 | int pathLength = path.size(); | ||
135 | if (shortestPathLength == -1 || pathLength < shortestPathLength) { | ||
136 | shortestPaths.clear(); | ||
137 | shortestPathLength = pathLength; | ||
138 | } | ||
139 | if (pathLength == shortestPathLength) { | ||
140 | shortestPaths.add(path); | ||
141 | } | ||
142 | } | ||
143 | return shortestPaths; | ||
144 | } | ||
145 | |||
146 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java new file mode 100644 index 00000000..cf68d36a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java | |||
@@ -0,0 +1,38 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc; | ||
11 | |||
12 | public class Edge<V> { | ||
13 | private V source; | ||
14 | private V target; | ||
15 | |||
16 | public Edge(V source, V target) { | ||
17 | super(); | ||
18 | this.source = source; | ||
19 | this.target = target; | ||
20 | } | ||
21 | |||
22 | public V getSource() { | ||
23 | return source; | ||
24 | } | ||
25 | |||
26 | public void setSource(V source) { | ||
27 | this.source = source; | ||
28 | } | ||
29 | |||
30 | public V getTarget() { | ||
31 | return target; | ||
32 | } | ||
33 | |||
34 | public void setTarget(V target) { | ||
35 | this.target = target; | ||
36 | } | ||
37 | |||
38 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java new file mode 100644 index 00000000..194e979b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java | |||
@@ -0,0 +1,173 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Tamas Szabo, 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.base.itc.alg.misc; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Map.Entry; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; | ||
19 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
20 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | |||
23 | /** | ||
24 | * Utility class for graph related operations. | ||
25 | * | ||
26 | * @author Tamas Szabo | ||
27 | */ | ||
28 | public class GraphHelper { | ||
29 | |||
30 | private GraphHelper() {/*Utility class constructor*/} | ||
31 | |||
32 | /** | ||
33 | * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes. | ||
34 | * | ||
35 | * @param nodesInSubGraph | ||
36 | * the nodes that are present in the subgraph | ||
37 | * @param graphDataSource | ||
38 | * the graph data source for the original graph | ||
39 | * @return the subgraph associated to the given nodes | ||
40 | */ | ||
41 | public static <V> Graph<V> getSubGraph(Collection<V> nodesInSubGraph, | ||
42 | IBiDirectionalGraphDataSource<V> graphDataSource) { | ||
43 | Graph<V> g = new Graph<V>(); | ||
44 | if (nodesInSubGraph != null) { | ||
45 | for (V node : nodesInSubGraph) { | ||
46 | g.insertNode(node); | ||
47 | } | ||
48 | |||
49 | for (V node : nodesInSubGraph) { | ||
50 | IMemoryView<V> sources = graphDataSource.getSourceNodes(node); | ||
51 | for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) { | ||
52 | for (int i = 0; i < entry.getValue(); i++) { | ||
53 | V s = entry.getKey(); | ||
54 | if (nodesInSubGraph.contains(s)) { | ||
55 | g.insertEdge(s, node); | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | return g; | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of | ||
67 | * nodes are used, this way it is possible to construct a path in a given subgraph. | ||
68 | * | ||
69 | * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph | ||
70 | * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times | ||
71 | * in the returned {@link List}. | ||
72 | * | ||
73 | * @param source | ||
74 | * the source node | ||
75 | * @param target | ||
76 | * the target node | ||
77 | * @param nodesInGraph | ||
78 | * the nodes that are present in the subgraph | ||
79 | * @param graphDataSource | ||
80 | * the graph data source | ||
81 | * @return the path between the two nodes | ||
82 | */ | ||
83 | public static <V> List<V> constructPath(V source, V target, Set<V> nodesInGraph, | ||
84 | IGraphDataSource<V> graphDataSource) { | ||
85 | Set<V> visitedNodes = new HashSet<V>(); | ||
86 | List<V> path = new ArrayList<V>(); | ||
87 | |||
88 | visitedNodes.add(source); | ||
89 | path.add(source); | ||
90 | V act = source; | ||
91 | |||
92 | // if source and target are the same node | ||
93 | if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) { | ||
94 | // the node will be present in the path two times | ||
95 | path.add(source); | ||
96 | return path; | ||
97 | } else { | ||
98 | while (act != null) { | ||
99 | V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes); | ||
100 | if (nextNode == null && path.size() > 1) { | ||
101 | // needs to backtrack along path | ||
102 | // remove the last element in the path because we can't go | ||
103 | // anywhere from there | ||
104 | path.remove(path.size() - 1); | ||
105 | while (nextNode == null && path.size() > 0) { | ||
106 | V lastPathElement = path.get(path.size() - 1); | ||
107 | nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes); | ||
108 | if (nextNode == null) { | ||
109 | path.remove(path.size() - 1); | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | if (nextNode != null) { | ||
115 | visitedNodes.add(nextNode); | ||
116 | path.add(nextNode); | ||
117 | if (nextNode.equals(target)) { | ||
118 | return path; | ||
119 | } | ||
120 | } | ||
121 | act = nextNode; | ||
122 | } | ||
123 | return null; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | private static <V> V getNextNodeToVisit(V act, IGraphDataSource<V> graphDataSource, Set<V> nodesInSubGraph, | ||
128 | Set<V> visitedNodes) { | ||
129 | IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(act); | ||
130 | for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) { | ||
131 | for (int i = 0; i < entry.getValue(); i++) { | ||
132 | V node = entry.getKey(); | ||
133 | if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) { | ||
134 | return node; | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | return null; | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Returns the number of self-loop edges for the given node. | ||
143 | * | ||
144 | * @param node | ||
145 | * the node | ||
146 | * @param graphDataSource | ||
147 | * the graph data source | ||
148 | * @return the number of self-loop edges | ||
149 | */ | ||
150 | public static <V> int getEdgeCount(V node, IGraphDataSource<V> graphDataSource) { | ||
151 | return getEdgeCount(node, node, graphDataSource); | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * Returns the number of edges between the given source and target nodes. | ||
156 | * | ||
157 | * @param source | ||
158 | * the source node | ||
159 | * @param target | ||
160 | * the target node | ||
161 | * @param graphDataSource | ||
162 | * the graph data source | ||
163 | * @return the number of parallel edges between the two nodes | ||
164 | */ | ||
165 | public static <V> int getEdgeCount(V source, V target, IGraphDataSource<V> graphDataSource) { | ||
166 | Integer count = graphDataSource.getTargetNodes(source).getCount(target); | ||
167 | if (count == null) { | ||
168 | return 0; | ||
169 | } else { | ||
170 | return count; | ||
171 | } | ||
172 | } | ||
173 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java new file mode 100644 index 00000000..cebb09c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java | |||
@@ -0,0 +1,67 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Abel Hegedus, 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.base.itc.alg.misc; | ||
10 | |||
11 | import java.util.Deque; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; | ||
15 | |||
16 | /** | ||
17 | * The path finder provides methods for retrieving paths in a graph between a source node and one or more target nodes. | ||
18 | * Use {@link ITcDataSource#getPathFinder()} for instantiating. | ||
19 | * | ||
20 | * @author Abel Hegedus | ||
21 | * | ||
22 | * @param <V> the node type of the graph | ||
23 | */ | ||
24 | public interface IGraphPathFinder<V> { | ||
25 | |||
26 | /** | ||
27 | * Returns an arbitrary path from the source node to the target node (if such exists). If there is no path | ||
28 | * between them, an empty collection is returned. | ||
29 | * | ||
30 | * @param sourceNode the source node of the path | ||
31 | * @param targetNode the target node of the path | ||
32 | * @return the path from the source to the target, or empty collection if target is not reachable from source. | ||
33 | */ | ||
34 | Deque<V> getPath(V sourceNode, V targetNode); | ||
35 | |||
36 | /** | ||
37 | * Returns the collection of shortest paths from the source node to the target node (if such exists). If there is no path | ||
38 | * between them, an empty collection is returned. | ||
39 | * | ||
40 | * @param sourceNode the source node of the path | ||
41 | * @param targetNode the target node of the path | ||
42 | * @return the collection of shortest paths from the source to the target, or empty collection if target is not reachable from source. | ||
43 | */ | ||
44 | Iterable<Deque<V>> getShortestPaths(V sourceNode, V targetNode); | ||
45 | |||
46 | /** | ||
47 | * Returns the collection of paths from the source node to the target node (if such exists). If there is no path | ||
48 | * between them, an empty collection is returned. | ||
49 | * | ||
50 | * @param sourceNode the source node of the path | ||
51 | * @param targetNode the target node of the path | ||
52 | * @return the collection of paths from the source to the target, or empty collection if target is not reachable from source. | ||
53 | */ | ||
54 | Iterable<Deque<V>> getAllPaths(V sourceNode, V targetNode); | ||
55 | |||
56 | /** | ||
57 | * Returns the collection of paths from the source node to any of the target nodes (if such exists). If there is no path | ||
58 | * between them, an empty collection is returned. | ||
59 | * | ||
60 | * @param sourceNode the source node of the path | ||
61 | * @param targetNodes the set of target nodes of the paths | ||
62 | * @return the collection of paths from the source to any of the targets, or empty collection if neither target is reachable from source. | ||
63 | */ | ||
64 | Iterable<Deque<V>> getAllPathsToTargets(V sourceNode, Set<V> targetNodes); | ||
65 | |||
66 | |||
67 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java new file mode 100644 index 00000000..a41ff6c7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | public interface ITcRelation<V> { | ||
15 | |||
16 | /** | ||
17 | * Returns the starting nodes from a transitive closure relation. | ||
18 | * | ||
19 | * @return the set of starting nodes | ||
20 | */ | ||
21 | public Set<V> getTupleStarts(); | ||
22 | |||
23 | /** | ||
24 | * Returns the set of nodes that are reachable from the given node. | ||
25 | * | ||
26 | * @param start | ||
27 | * the starting node | ||
28 | * @return the set of reachable nodes | ||
29 | */ | ||
30 | public Set<V> getTupleEnds(V start); | ||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java new file mode 100644 index 00000000..a2d54a59 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java | |||
@@ -0,0 +1,60 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc; | ||
11 | |||
12 | public class Tuple<V> { | ||
13 | |||
14 | private V source; | ||
15 | private V target; | ||
16 | |||
17 | public Tuple(V source, V target) { | ||
18 | super(); | ||
19 | this.source = source; | ||
20 | this.target = target; | ||
21 | } | ||
22 | |||
23 | public V getSource() { | ||
24 | return source; | ||
25 | } | ||
26 | |||
27 | public void setSource(V source) { | ||
28 | this.source = source; | ||
29 | } | ||
30 | |||
31 | public V getTarget() { | ||
32 | return target; | ||
33 | } | ||
34 | |||
35 | public void setTarget(V target) { | ||
36 | this.target = target; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public String toString() { | ||
41 | return "(" + source.toString() + "," + target.toString() + ")"; | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public boolean equals(Object o) { | ||
46 | if (o instanceof Tuple) { | ||
47 | Tuple<?> t = (Tuple<?>) o; | ||
48 | |||
49 | if (t.getSource().equals(this.source) && t.getTarget().equals(this.target)) { | ||
50 | return true; | ||
51 | } | ||
52 | } | ||
53 | return false; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public int hashCode() { | ||
58 | return source.hashCode() + target.hashCode(); | ||
59 | } | ||
60 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java new file mode 100644 index 00000000..798f31d2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java | |||
@@ -0,0 +1,151 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc.bfs; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
18 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
19 | |||
20 | public class BFS<V> { | ||
21 | |||
22 | private BFS() {/*Utility class constructor*/} | ||
23 | |||
24 | /** | ||
25 | * Performs a breadth first search on the given graph to determine whether source is reachable from target. | ||
26 | * | ||
27 | * @param <V> | ||
28 | * the type parameter of the nodes in the graph | ||
29 | * @param source | ||
30 | * the source node | ||
31 | * @param target | ||
32 | * the target node | ||
33 | * @param graph | ||
34 | * the graph data source | ||
35 | * @return true if source is reachable from target, false otherwise | ||
36 | */ | ||
37 | public static <V> boolean isReachable(V source, V target, IGraphDataSource<V> graph) { | ||
38 | List<V> nodeQueue = new ArrayList<V>(); | ||
39 | Set<V> visited = new HashSet<V>(); | ||
40 | |||
41 | nodeQueue.add(source); | ||
42 | visited.add(source); | ||
43 | |||
44 | boolean ret = _isReachable(target, graph, nodeQueue, visited); | ||
45 | return ret; | ||
46 | } | ||
47 | |||
48 | private static <V> boolean _isReachable(V target, IGraphDataSource<V> graph, List<V> nodeQueue, Set<V> visited) { | ||
49 | |||
50 | while (!nodeQueue.isEmpty()) { | ||
51 | V node = nodeQueue.remove(0); | ||
52 | for (V t : graph.getTargetNodes(node).distinctValues()){ | ||
53 | if (t.equals(target)) { | ||
54 | return true; | ||
55 | } | ||
56 | if (!visited.contains(t)) { | ||
57 | visited.add(t); | ||
58 | nodeQueue.add(t); | ||
59 | } | ||
60 | } | ||
61 | } | ||
62 | return false; | ||
63 | } | ||
64 | |||
65 | public static <V> Set<V> reachableSources(IBiDirectionalGraphDataSource<V> graph, V target) { | ||
66 | Set<V> retSet = new HashSet<V>(); | ||
67 | retSet.add(target); | ||
68 | List<V> nodeQueue = new ArrayList<V>(); | ||
69 | nodeQueue.add(target); | ||
70 | |||
71 | _reachableSources(graph, nodeQueue, retSet); | ||
72 | |||
73 | return retSet; | ||
74 | } | ||
75 | |||
76 | private static <V> void _reachableSources(IBiDirectionalGraphDataSource<V> graph, List<V> nodeQueue, | ||
77 | Set<V> retSet) { | ||
78 | while (!nodeQueue.isEmpty()) { | ||
79 | V node = nodeQueue.remove(0); | ||
80 | for (V _node : graph.getSourceNodes(node).distinctValues()) { | ||
81 | if (!retSet.contains(_node)) { | ||
82 | retSet.add(_node); | ||
83 | nodeQueue.add(_node); | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | public static <V> Set<V> reachableTargets(IGraphDataSource<V> graph, V source) { | ||
90 | Set<V> retSet = new HashSet<V>(); | ||
91 | retSet.add(source); | ||
92 | List<V> nodeQueue = new ArrayList<V>(); | ||
93 | nodeQueue.add(source); | ||
94 | |||
95 | _reachableTargets(graph, nodeQueue, retSet); | ||
96 | |||
97 | return retSet; | ||
98 | } | ||
99 | |||
100 | private static <V> void _reachableTargets(IGraphDataSource<V> graph, List<V> nodeQueue, Set<V> retSet) { | ||
101 | while (!nodeQueue.isEmpty()) { | ||
102 | V node = nodeQueue.remove(0); | ||
103 | |||
104 | for (V _node : graph.getTargetNodes(node).distinctValues()) { | ||
105 | |||
106 | if (!retSet.contains(_node)) { | ||
107 | retSet.add(_node); | ||
108 | nodeQueue.add(_node); | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Performs a breadth first search on the given graph and collects all the nodes along the path from source to | ||
116 | * target if such path exists. | ||
117 | * | ||
118 | * @param <V> | ||
119 | * the type parameter of the nodes in the graph | ||
120 | * @param source | ||
121 | * the source node | ||
122 | * @param target | ||
123 | * the target node | ||
124 | * @param graph | ||
125 | * the graph data source | ||
126 | * @return the set of nodes along the path | ||
127 | */ | ||
128 | public static <V> Set<V> collectNodesAlongPath(V source, V target, IGraphDataSource<V> graph) { | ||
129 | Set<V> path = new HashSet<V>(); | ||
130 | _collectNodesAlongPath(source, target, graph, path); | ||
131 | return path; | ||
132 | } | ||
133 | |||
134 | private static <V> boolean _collectNodesAlongPath(V node, V target, IGraphDataSource<V> graph, Set<V> path) { | ||
135 | |||
136 | boolean res = false; | ||
137 | |||
138 | // end recursion | ||
139 | if (node.equals(target)) { | ||
140 | path.add(node); | ||
141 | return true; | ||
142 | } else { | ||
143 | for (V _nodeT : graph.getTargetNodes(node).distinctValues()) { | ||
144 | res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res; | ||
145 | } | ||
146 | if (res) | ||
147 | path.add(node); | ||
148 | return res; | ||
149 | } | ||
150 | } | ||
151 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java new file mode 100644 index 00000000..c8d25c4e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java | |||
@@ -0,0 +1,93 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc.dfs; | ||
11 | |||
12 | import java.util.HashMap; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; | ||
15 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
16 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
17 | |||
18 | public class DFSAlg<V> implements IGraphObserver<V> { | ||
19 | |||
20 | private IGraphDataSource<V> gds; | ||
21 | private DRedTcRelation<V> tc; | ||
22 | private int[] visited; | ||
23 | private HashMap<V, Integer> nodeMap; | ||
24 | |||
25 | public DFSAlg(IGraphDataSource<V> gds) { | ||
26 | this.gds = gds; | ||
27 | this.tc = new DRedTcRelation<V>(); | ||
28 | gds.attachObserver(this); | ||
29 | deriveTc(); | ||
30 | } | ||
31 | |||
32 | private void deriveTc() { | ||
33 | tc.clear(); | ||
34 | |||
35 | this.visited = new int[gds.getAllNodes().size()]; | ||
36 | nodeMap = new HashMap<V, Integer>(); | ||
37 | |||
38 | int j = 0; | ||
39 | for (V n : gds.getAllNodes()) { | ||
40 | nodeMap.put(n, j); | ||
41 | j++; | ||
42 | } | ||
43 | |||
44 | for (V n : gds.getAllNodes()) { | ||
45 | oneDFS(n, n); | ||
46 | initVisitedArray(); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | private void initVisitedArray() { | ||
51 | for (int i = 0; i < visited.length; i++) | ||
52 | visited[i] = 0; | ||
53 | } | ||
54 | |||
55 | private void oneDFS(V act, V source) { | ||
56 | |||
57 | if (!act.equals(source)) { | ||
58 | tc.addTuple(source, act); | ||
59 | } | ||
60 | |||
61 | visited[nodeMap.get(act)] = 1; | ||
62 | |||
63 | for (V t : gds.getTargetNodes(act).distinctValues()) { | ||
64 | if (visited[nodeMap.get(t)] == 0) { | ||
65 | oneDFS(t, source); | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | public DRedTcRelation<V> getTcRelation() { | ||
71 | return this.tc; | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void edgeInserted(V source, V target) { | ||
76 | deriveTc(); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public void edgeDeleted(V source, V target) { | ||
81 | deriveTc(); | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public void nodeInserted(V n) { | ||
86 | deriveTc(); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public void nodeDeleted(V n) { | ||
91 | deriveTc(); | ||
92 | } | ||
93 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java new file mode 100644 index 00000000..c99a48ab --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java | |||
@@ -0,0 +1,179 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
19 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; | ||
20 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
21 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
22 | |||
23 | public class PKAlg<V> implements IGraphObserver<V> { | ||
24 | |||
25 | /** | ||
26 | * Maps the nodes to their indicies. | ||
27 | */ | ||
28 | private Map<V, Integer> node2index; | ||
29 | private Map<Integer, V> index2node; | ||
30 | private Map<V, Boolean> node2mark; | ||
31 | |||
32 | /** | ||
33 | * Maps the index of a node to the index in the topsort. | ||
34 | */ | ||
35 | private Map<Integer, Integer> index2topsort; | ||
36 | private Map<Integer, Integer> topsort2index; | ||
37 | |||
38 | /** | ||
39 | * Index associated to the inserted nodes (incrementing with every insertion). | ||
40 | */ | ||
41 | private int index; | ||
42 | |||
43 | /** | ||
44 | * Index within the topsort for the target node when edge insertion occurs. | ||
45 | */ | ||
46 | private int lower_bound; | ||
47 | |||
48 | /** | ||
49 | * Index within the topsort for the source node when edge insertion occurs. | ||
50 | */ | ||
51 | private int upper_bound; | ||
52 | |||
53 | private List<V> RF; | ||
54 | private List<V> RB; | ||
55 | private IBiDirectionalGraphDataSource<V> gds; | ||
56 | |||
57 | public PKAlg(IGraphDataSource<V> gds) { | ||
58 | if (gds instanceof IBiDirectionalGraphDataSource<?>) { | ||
59 | this.gds = (IBiDirectionalGraphDataSource<V>) gds; | ||
60 | } else { | ||
61 | this.gds = new IBiDirectionalWrapper<V>(gds); | ||
62 | } | ||
63 | |||
64 | node2mark = new HashMap<V, Boolean>(); | ||
65 | node2index = new HashMap<V, Integer>(); | ||
66 | index2node = new HashMap<Integer, V>(); | ||
67 | index2topsort = new HashMap<Integer, Integer>(); | ||
68 | topsort2index = new HashMap<Integer, Integer>(); | ||
69 | index = 0; | ||
70 | |||
71 | gds.attachObserver(this); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void edgeInserted(V source, V target) { | ||
76 | |||
77 | RF = new ArrayList<V>(); | ||
78 | RB = new ArrayList<V>(); | ||
79 | |||
80 | lower_bound = index2topsort.get(node2index.get(target)); | ||
81 | upper_bound = index2topsort.get(node2index.get(source)); | ||
82 | |||
83 | if (lower_bound < upper_bound) { | ||
84 | dfsForward(target); | ||
85 | dfsBackward(source); | ||
86 | reorder(); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | private List<Integer> getIndicies(List<V> list) { | ||
91 | List<Integer> indicies = new ArrayList<Integer>(); | ||
92 | |||
93 | for (V n : list) | ||
94 | indicies.add(index2topsort.get(node2index.get(n))); | ||
95 | |||
96 | return indicies; | ||
97 | } | ||
98 | |||
99 | private void reorder() { | ||
100 | |||
101 | Collections.reverse(RB); | ||
102 | |||
103 | // azon csomopontok indexei amelyek sorrendje nem jo | ||
104 | List<Integer> L = getIndicies(RF); | ||
105 | L.addAll(getIndicies(RB)); | ||
106 | Collections.sort(L); | ||
107 | |||
108 | for (int i = 0; i < RB.size(); i++) { | ||
109 | index2topsort.put(node2index.get(RB.get(i)), L.get(i)); | ||
110 | topsort2index.put(L.get(i), node2index.get(RB.get(i))); | ||
111 | } | ||
112 | |||
113 | for (int i = 0; i < RF.size(); i++) { | ||
114 | index2topsort.put(node2index.get(RF.get(i)), L.get(i + RB.size())); | ||
115 | topsort2index.put(L.get(i + RB.size()), node2index.get(RF.get(i))); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | @SuppressWarnings("unused") | ||
120 | private List<V> getTopSort() { | ||
121 | List<V> topsort = new ArrayList<V>(); | ||
122 | |||
123 | for (int i : topsort2index.values()) { | ||
124 | topsort.add(index2node.get(i)); | ||
125 | } | ||
126 | |||
127 | return topsort; | ||
128 | } | ||
129 | |||
130 | private void dfsBackward(V node) { | ||
131 | node2mark.put(node, true); | ||
132 | RB.add(node); | ||
133 | |||
134 | for (V sn : gds.getSourceNodes(node).distinctValues()) { | ||
135 | int top_id = index2topsort.get(node2index.get(sn)); | ||
136 | |||
137 | if (!node2mark.get(sn) && lower_bound < top_id) | ||
138 | dfsBackward(sn); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | private void dfsForward(V node) { | ||
143 | node2mark.put(node, true); | ||
144 | RF.add(node); | ||
145 | |||
146 | for (V tn : gds.getTargetNodes(node).distinctValues()) { | ||
147 | int top_id = index2topsort.get(node2index.get(tn)); | ||
148 | |||
149 | if (top_id == upper_bound) | ||
150 | System.out.println("!!!Cycle detected!!!"); | ||
151 | else if (!node2mark.get(tn) && top_id < upper_bound) | ||
152 | dfsForward(tn); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | @Override | ||
157 | public void edgeDeleted(V source, V target) { | ||
158 | // Edge deletion does not affect topsort | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public void nodeInserted(V n) { | ||
163 | node2mark.put(n, false); | ||
164 | node2index.put(n, index); | ||
165 | index2node.put(index, n); | ||
166 | index2topsort.put(index, index); | ||
167 | topsort2index.put(index, index); | ||
168 | index++; | ||
169 | } | ||
170 | |||
171 | @Override | ||
172 | public void nodeDeleted(V n) { | ||
173 | node2mark.remove(n); | ||
174 | int node_id = node2index.remove(n); | ||
175 | index2node.remove(node_id); | ||
176 | int top_id = index2topsort.remove(node_id); | ||
177 | topsort2index.remove(top_id); | ||
178 | } | ||
179 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java new file mode 100644 index 00000000..8915998b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java | |||
@@ -0,0 +1,146 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | import java.util.Stack; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | |||
20 | /** | ||
21 | * Efficient algorithms to compute the Strongly Connected Components in a directed graph. | ||
22 | * | ||
23 | * @author Tamas Szabo | ||
24 | * | ||
25 | * @param <V> | ||
26 | * the type parameter of the nodes in the graph | ||
27 | */ | ||
28 | public class SCC<V> { | ||
29 | |||
30 | private SCC() {/*Utility class constructor*/} | ||
31 | |||
32 | public static long sccId = 0; | ||
33 | |||
34 | /** | ||
35 | * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm) | ||
36 | * | ||
37 | * @param g | ||
38 | * the directed graph data source | ||
39 | * @return the set of SCCs | ||
40 | */ | ||
41 | public static <V> SCCResult<V> computeSCC(IGraphDataSource<V> g) { | ||
42 | int index = 0; | ||
43 | Set<Set<V>> ret = new HashSet<Set<V>>(); | ||
44 | |||
45 | // stores the lowlink and index information for the given node | ||
46 | Map<V, SCCProperty> nodeMap = CollectionsFactory.createMap(); | ||
47 | |||
48 | // stores all target nodes of a given node - the list will be modified | ||
49 | Map<V, Set<V>> targetNodeMap = CollectionsFactory.createMap(); | ||
50 | |||
51 | // stores those target nodes for a given node which have not been visited | ||
52 | Map<V, Set<V>> notVisitedMap = CollectionsFactory.createMap(); | ||
53 | |||
54 | // stores the nodes during the traversal | ||
55 | Stack<V> nodeStack = new Stack<V>(); | ||
56 | |||
57 | // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time) | ||
58 | Stack<V> sccStack = new Stack<V>(); | ||
59 | |||
60 | boolean sink = false, finishedTraversal = true; | ||
61 | |||
62 | // initialize all nodes with 0 index and 0 lowlink | ||
63 | Set<V> allNodes = g.getAllNodes(); | ||
64 | for (V n : allNodes) { | ||
65 | nodeMap.put(n, new SCCProperty(0, 0)); | ||
66 | } | ||
67 | |||
68 | for (V n : allNodes) { | ||
69 | // if the node has not been visited yet | ||
70 | if (nodeMap.get(n).getIndex() == 0) { | ||
71 | nodeStack.push(n); | ||
72 | |||
73 | while (!nodeStack.isEmpty()) { | ||
74 | V currentNode = nodeStack.peek(); | ||
75 | sink = false; | ||
76 | finishedTraversal = false; | ||
77 | SCCProperty prop = nodeMap.get(currentNode); | ||
78 | |||
79 | if (nodeMap.get(currentNode).getIndex() == 0) { | ||
80 | index++; | ||
81 | sccStack.push(currentNode); | ||
82 | prop.setIndex(index); | ||
83 | prop.setLowlink(index); | ||
84 | |||
85 | notVisitedMap.put(currentNode, new HashSet<V>()); | ||
86 | |||
87 | // storing the target nodes of the actual node | ||
88 | if (g.getTargetNodes(currentNode) != null) { | ||
89 | Set<V> targets = g.getTargetNodes(currentNode).distinctValues(); | ||
90 | targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets)); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | if (targetNodeMap.get(currentNode) != null) { | ||
95 | |||
96 | // remove node from stack, the exploration of its children has finished | ||
97 | if (targetNodeMap.get(currentNode).size() == 0) { | ||
98 | targetNodeMap.remove(currentNode); | ||
99 | |||
100 | nodeStack.pop(); | ||
101 | |||
102 | for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) { | ||
103 | if (notVisitedMap.get(currentNode).contains(targetNode)) { | ||
104 | prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink())); | ||
105 | } else if (sccStack.contains(targetNode)) { | ||
106 | prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex())); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | finishedTraversal = true; | ||
111 | } else { | ||
112 | V targetNode = targetNodeMap.get(currentNode).iterator().next(); | ||
113 | targetNodeMap.get(currentNode).remove(targetNode); | ||
114 | // if the targetNode has not yet been visited push it to the stack | ||
115 | // and mark it in the notVisitedMap | ||
116 | if (nodeMap.get(targetNode).getIndex() == 0) { | ||
117 | notVisitedMap.get(currentNode).add(targetNode); | ||
118 | nodeStack.add(targetNode); | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | // if currentNode has no target nodes | ||
123 | else { | ||
124 | nodeStack.pop(); | ||
125 | sink = true; | ||
126 | } | ||
127 | |||
128 | // create scc if node is a sink or an scc has been found | ||
129 | if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) { | ||
130 | Set<V> sc = new HashSet<V>(); | ||
131 | V targetNode = null; | ||
132 | |||
133 | do { | ||
134 | targetNode = sccStack.pop(); | ||
135 | sc.add(targetNode); | ||
136 | } while (!targetNode.equals(currentNode)); | ||
137 | |||
138 | ret.add(sc); | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | return new SCCResult<V>(ret, g); | ||
145 | } | ||
146 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java new file mode 100644 index 00000000..b26e3d37 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; | ||
11 | |||
12 | public class SCCProperty { | ||
13 | private int index; | ||
14 | private int lowlink; | ||
15 | |||
16 | public SCCProperty(int index, int lowlink) { | ||
17 | super(); | ||
18 | this.index = index; | ||
19 | this.lowlink = lowlink; | ||
20 | } | ||
21 | |||
22 | public int getIndex() { | ||
23 | return index; | ||
24 | } | ||
25 | |||
26 | public void setIndex(int index) { | ||
27 | this.index = index; | ||
28 | } | ||
29 | |||
30 | public int getLowlink() { | ||
31 | return lowlink; | ||
32 | } | ||
33 | |||
34 | public void setLowlink(int lowlink) { | ||
35 | this.lowlink = lowlink; | ||
36 | } | ||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java new file mode 100644 index 00000000..fde59d3b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java | |||
@@ -0,0 +1,81 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; | ||
11 | |||
12 | import java.util.Map.Entry; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
16 | |||
17 | public class SCCResult<V> { | ||
18 | |||
19 | private Set<Set<V>> sccs; | ||
20 | private IGraphDataSource<V> gds; | ||
21 | |||
22 | public SCCResult(Set<Set<V>> sccs, IGraphDataSource<V> gds) { | ||
23 | this.sccs = sccs; | ||
24 | this.gds = gds; | ||
25 | } | ||
26 | |||
27 | public Set<Set<V>> getSccs() { | ||
28 | return sccs; | ||
29 | } | ||
30 | |||
31 | public int getSCCCount() { | ||
32 | return sccs.size(); | ||
33 | } | ||
34 | |||
35 | public double getAverageNodeCount() { | ||
36 | double a = 0; | ||
37 | |||
38 | for (Set<V> s : sccs) { | ||
39 | a += s.size(); | ||
40 | } | ||
41 | |||
42 | return a / sccs.size(); | ||
43 | } | ||
44 | |||
45 | public double getAverageEdgeCount() { | ||
46 | long edgeSum = 0; | ||
47 | |||
48 | for (Set<V> scc : sccs) { | ||
49 | for (V source : scc) { | ||
50 | for (Entry<V, Integer> entry : gds.getTargetNodes(source).entriesWithMultiplicities()) { | ||
51 | if (scc.contains(entry.getKey())) { | ||
52 | edgeSum += entry.getValue(); | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | return (double) edgeSum / (double) sccs.size(); | ||
59 | } | ||
60 | |||
61 | public int getBiggestSCCSize() { | ||
62 | int max = 0; | ||
63 | |||
64 | for (Set<V> scc : sccs) { | ||
65 | if (scc.size() > max) | ||
66 | max = scc.size(); | ||
67 | } | ||
68 | |||
69 | return max; | ||
70 | } | ||
71 | |||
72 | public long getSumOfSquares() { | ||
73 | long sum = 0; | ||
74 | |||
75 | for (Set<V> scc : sccs) { | ||
76 | sum += scc.size() * scc.size(); | ||
77 | } | ||
78 | |||
79 | return sum; | ||
80 | } | ||
81 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java new file mode 100644 index 00000000..dd18e6c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java | |||
@@ -0,0 +1,77 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.alg.misc.topsort; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.LinkedList; | ||
14 | import java.util.List; | ||
15 | import java.util.Set; | ||
16 | import java.util.Stack; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
19 | |||
20 | /** | ||
21 | * @since 1.6 | ||
22 | */ | ||
23 | public class TopologicalSorting { | ||
24 | |||
25 | private TopologicalSorting() {/*Utility class constructor*/} | ||
26 | |||
27 | private static final class Pair<T> { | ||
28 | public T element; | ||
29 | public boolean isParent; | ||
30 | |||
31 | public Pair(final T element, final boolean isParent) { | ||
32 | this.element = element; | ||
33 | this.isParent = isParent; | ||
34 | } | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * Returns a topological ordering for the given graph data source. | ||
39 | * Output format: if there is an a -> b (transitive) reachability, then node <code>a</code> will come before node <code>b</code> in the resulting list. | ||
40 | * | ||
41 | * @param gds the graph data source | ||
42 | * @return a topological ordering | ||
43 | */ | ||
44 | public static <T> List<T> compute(final IGraphDataSource<T> gds) { | ||
45 | final Set<T> visited = new HashSet<T>(); | ||
46 | final LinkedList<T> result = new LinkedList<T>(); | ||
47 | final Stack<Pair<T>> dfsStack = new Stack<Pair<T>>(); | ||
48 | |||
49 | for (final T node : gds.getAllNodes()) { | ||
50 | if (!visited.contains(node)) { | ||
51 | dfsStack.push(new Pair<T>(node, false)); | ||
52 | } | ||
53 | |||
54 | while (!dfsStack.isEmpty()) { | ||
55 | final Pair<T> head = dfsStack.pop(); | ||
56 | final T source = head.element; | ||
57 | |||
58 | if (head.isParent) { | ||
59 | // we have already seen source, push it to the resulting stack | ||
60 | result.addFirst(source); | ||
61 | } else { | ||
62 | // first time we see source, continue with its children | ||
63 | visited.add(source); | ||
64 | dfsStack.push(new Pair<T>(source, true)); | ||
65 | |||
66 | for (final T target : gds.getTargetNodes(source).distinctValues()) { | ||
67 | if (!visited.contains(target)) { | ||
68 | dfsStack.push(new Pair<T>(target, false)); | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | return result; | ||
76 | } | ||
77 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java new file mode 100644 index 00000000..51015404 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.base.itc.alg.representative; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; | ||
9 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
10 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
11 | |||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | public abstract class RepresentativeElectionAlgorithm implements IGraphObserver<Object> { | ||
18 | protected final Graph<Object> graph; | ||
19 | protected final Map<Object, Object> representatives = new HashMap<>(); | ||
20 | protected final Map<Object, Set<Object>> components = new HashMap<>(); | ||
21 | private RepresentativeObserver observer; | ||
22 | |||
23 | protected RepresentativeElectionAlgorithm(Graph<Object> graph) { | ||
24 | this.graph = graph; | ||
25 | initializeComponents(); | ||
26 | graph.attachObserver(this); | ||
27 | } | ||
28 | |||
29 | protected abstract void initializeComponents(); | ||
30 | |||
31 | protected void initializeSet(Set<Object> set) { | ||
32 | var iterator = set.iterator(); | ||
33 | if (!iterator.hasNext()) { | ||
34 | // Set is empty. | ||
35 | return; | ||
36 | } | ||
37 | var representative = iterator.next(); | ||
38 | for (var node : set) { | ||
39 | var oldRepresentative = representatives.put(node, representative); | ||
40 | if (oldRepresentative != null && !representative.equals(oldRepresentative)) { | ||
41 | throw new IllegalStateException("Node %s is already in a set represented by %s, cannot add it to %s" | ||
42 | .formatted(node, oldRepresentative, set)); | ||
43 | } | ||
44 | } | ||
45 | components.put(representative, set); | ||
46 | } | ||
47 | |||
48 | protected void merge(Object leftRepresentative, Object rightRepresentative) { | ||
49 | if (leftRepresentative.equals(rightRepresentative)) { | ||
50 | return; | ||
51 | } | ||
52 | var leftSet = getComponent(leftRepresentative); | ||
53 | var rightSet = getComponent(rightRepresentative); | ||
54 | if (leftSet.size() < rightSet.size()) { | ||
55 | merge(rightRepresentative, rightSet, leftRepresentative, leftSet); | ||
56 | } else { | ||
57 | merge(leftRepresentative, leftSet, rightRepresentative, rightSet); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | private void merge(Object preservedRepresentative, Set<Object> preservedSet, Object removedRepresentative, | ||
62 | Set<Object> removedSet) { | ||
63 | components.remove(removedRepresentative); | ||
64 | for (var node : removedSet) { | ||
65 | representatives.put(node, preservedRepresentative); | ||
66 | preservedSet.add(node); | ||
67 | notifyToObservers(node, removedRepresentative, preservedRepresentative); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | protected void assignNewRepresentative(Object oldRepresentative, Set<Object> set) { | ||
72 | var iterator = set.iterator(); | ||
73 | if (!iterator.hasNext()) { | ||
74 | return; | ||
75 | } | ||
76 | var newRepresentative = iterator.next(); | ||
77 | components.put(newRepresentative, set); | ||
78 | for (var node : set) { | ||
79 | var oldRepresentativeOfNode = representatives.put(node, newRepresentative); | ||
80 | if (!oldRepresentative.equals(oldRepresentativeOfNode)) { | ||
81 | throw new IllegalArgumentException("Node %s was not represented by %s but by %s" | ||
82 | .formatted(node, oldRepresentative, oldRepresentativeOfNode)); | ||
83 | } | ||
84 | notifyToObservers(node, oldRepresentative, newRepresentative); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | public void setObserver(RepresentativeObserver observer) { | ||
89 | this.observer = observer; | ||
90 | } | ||
91 | |||
92 | public Map<Object, Set<Object>> getComponents() { | ||
93 | return components; | ||
94 | } | ||
95 | |||
96 | public Object getRepresentative(Object node) { | ||
97 | return representatives.get(node); | ||
98 | } | ||
99 | |||
100 | public Set<Object> getComponent(Object representative) { | ||
101 | return components.get(representative); | ||
102 | } | ||
103 | |||
104 | public void dispose() { | ||
105 | graph.detachObserver(this); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public void nodeInserted(Object n) { | ||
110 | var component = new HashSet<>(1); | ||
111 | component.add(n); | ||
112 | initializeSet(component); | ||
113 | notifyToObservers(n, n, Direction.INSERT); | ||
114 | } | ||
115 | |||
116 | @Override | ||
117 | public void nodeDeleted(Object n) { | ||
118 | var representative = representatives.remove(n); | ||
119 | if (!representative.equals(n)) { | ||
120 | throw new IllegalStateException("Trying to delete node with dangling edges"); | ||
121 | } | ||
122 | components.remove(representative); | ||
123 | notifyToObservers(n, representative, Direction.DELETE); | ||
124 | } | ||
125 | |||
126 | protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) { | ||
127 | notifyToObservers(node, oldRepresentative, Direction.DELETE); | ||
128 | notifyToObservers(node, newRepresentative, Direction.INSERT); | ||
129 | } | ||
130 | |||
131 | protected void notifyToObservers(Object node, Object representative, Direction direction) { | ||
132 | if (observer != null) { | ||
133 | observer.tupleChanged(node, representative, direction); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | public interface Factory { | ||
138 | RepresentativeElectionAlgorithm create(Graph<Object> graph); | ||
139 | } | ||
140 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java new file mode 100644 index 00000000..93cce1ea --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.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.viatra.runtime.base.itc.alg.representative; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
9 | |||
10 | public interface RepresentativeObserver { | ||
11 | void tupleChanged(Object node, Object representative, Direction direction); | ||
12 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..ba42bb13 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.base.itc.alg.representative; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; | ||
9 | import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; | ||
10 | import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; | ||
11 | import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; | ||
12 | |||
13 | import java.util.Collection; | ||
14 | import java.util.Set; | ||
15 | |||
16 | public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { | ||
17 | public StronglyConnectedComponentAlgorithm(Graph<Object> graph) { | ||
18 | super(graph); | ||
19 | } | ||
20 | |||
21 | @Override | ||
22 | protected void initializeComponents() { | ||
23 | var computedSCCs = SCC.computeSCC(graph).getSccs(); | ||
24 | for (var computedSCC : computedSCCs) { | ||
25 | initializeSet(computedSCC); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public void edgeInserted(Object source, Object target) { | ||
31 | var sourceRoot = getRepresentative(source); | ||
32 | var targetRoot = getRepresentative(target); | ||
33 | if (sourceRoot.equals(targetRoot)) { | ||
34 | // New edge does not change strongly connected components. | ||
35 | return; | ||
36 | } | ||
37 | if (BFS.isReachable(target, source, graph)) { | ||
38 | merge(sourceRoot, targetRoot); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public void edgeDeleted(Object source, Object target) { | ||
44 | var sourceRoot = getRepresentative(source); | ||
45 | var targetRoot = getRepresentative(target); | ||
46 | if (!sourceRoot.equals(targetRoot)) { | ||
47 | // New edge does not change strongly connected components. | ||
48 | return; | ||
49 | } | ||
50 | var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph); | ||
51 | if (!BFS.isReachable(source, target, component)) { | ||
52 | var newSCCs = SCC.computeSCC(component).getSccs(); | ||
53 | split(sourceRoot, newSCCs); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | private void split(Object preservedRepresentative, Collection<? extends Set<Object>> sets) { | ||
58 | for (var set : sets) { | ||
59 | if (set.contains(preservedRepresentative)) { | ||
60 | components.put(preservedRepresentative, set); | ||
61 | } else { | ||
62 | assignNewRepresentative(preservedRepresentative, set); | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..22159499 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.base.itc.alg.representative; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; | ||
9 | |||
10 | import java.util.ArrayDeque; | ||
11 | import java.util.HashSet; | ||
12 | import java.util.Set; | ||
13 | |||
14 | public class WeaklyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { | ||
15 | public WeaklyConnectedComponentAlgorithm(Graph<Object> graph) { | ||
16 | super(graph); | ||
17 | } | ||
18 | |||
19 | @Override | ||
20 | protected void initializeComponents() { | ||
21 | for (var node : graph.getAllNodes()) { | ||
22 | if (representatives.containsKey(node)) { | ||
23 | continue; | ||
24 | } | ||
25 | var reachable = getReachableNodes(node); | ||
26 | initializeSet(reachable); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public void edgeInserted(Object source, Object target) { | ||
32 | var sourceRoot = getRepresentative(source); | ||
33 | var targetRoot = getRepresentative(target); | ||
34 | merge(sourceRoot, targetRoot); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public void edgeDeleted(Object source, Object target) { | ||
39 | var sourceRoot = getRepresentative(source); | ||
40 | var targetRoot = getRepresentative(target); | ||
41 | if (!sourceRoot.equals(targetRoot)) { | ||
42 | throw new IllegalArgumentException("Trying to remove edge not in graph"); | ||
43 | } | ||
44 | var targetReachable = getReachableNodes(target); | ||
45 | if (!targetReachable.contains(source)) { | ||
46 | split(sourceRoot, targetReachable); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | private void split(Object sourceRepresentative, Set<Object> targetReachable) { | ||
51 | var sourceComponent = getComponent(sourceRepresentative); | ||
52 | sourceComponent.removeAll(targetReachable); | ||
53 | if (targetReachable.contains(sourceRepresentative)) { | ||
54 | components.put(sourceRepresentative, targetReachable); | ||
55 | assignNewRepresentative(sourceRepresentative, sourceComponent); | ||
56 | } else { | ||
57 | assignNewRepresentative(sourceRepresentative, targetReachable); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | private Set<Object> getReachableNodes(Object source) { | ||
62 | var retSet = new HashSet<>(); | ||
63 | retSet.add(source); | ||
64 | var nodeQueue = new ArrayDeque<>(); | ||
65 | nodeQueue.addLast(source); | ||
66 | |||
67 | while (!nodeQueue.isEmpty()) { | ||
68 | var node = nodeQueue.removeFirst(); | ||
69 | for (var neighbor : graph.getTargetNodes(node).distinctValues()) { | ||
70 | if (!retSet.contains(neighbor)) { | ||
71 | retSet.add(neighbor); | ||
72 | nodeQueue.addLast(neighbor); | ||
73 | } | ||
74 | } | ||
75 | for (var neighbor : graph.getSourceNodes(node).distinctValues()) { | ||
76 | if (!retSet.contains(neighbor)) { | ||
77 | retSet.add(neighbor); | ||
78 | nodeQueue.addLast(neighbor); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | return retSet; | ||
84 | } | ||
85 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java new file mode 100644 index 00000000..c9b3cafa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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.base.itc.alg.util; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
14 | |||
15 | /** | ||
16 | * @author Tamas Szabo | ||
17 | * | ||
18 | */ | ||
19 | public class CollectionHelper { | ||
20 | |||
21 | private CollectionHelper() {/*Utility class constructor*/} | ||
22 | |||
23 | /** | ||
24 | * Returns the intersection of two sets. It calls {@link Set#retainAll(java.util.Collection)} but returns a new set | ||
25 | * containing the elements of the intersection. | ||
26 | * | ||
27 | * @param set1 | ||
28 | * the first set (can be null, interpreted as empty) | ||
29 | * @param set2 | ||
30 | * the second set (can be null, interpreted as empty) | ||
31 | * @return the intersection of the sets | ||
32 | * @since 1.7 | ||
33 | */ | ||
34 | public static <V> Set<V> intersection(Set<V> set1, Set<V> set2) { | ||
35 | if (set1 == null || set2 == null) | ||
36 | return CollectionsFactory.createSet(); | ||
37 | |||
38 | Set<V> intersection = CollectionsFactory.createSet(set1); | ||
39 | intersection.retainAll(set2); | ||
40 | return intersection; | ||
41 | } | ||
42 | |||
43 | |||
44 | /** | ||
45 | * Returns the difference of two sets (S1\S2). It calls {@link Set#removeAll(java.util.Collection)} but returns a | ||
46 | * new set containing the elements of the difference. | ||
47 | * | ||
48 | * @param set1 | ||
49 | * the first set (can be null, interpreted as empty) | ||
50 | * @param set2 | ||
51 | * the second set (can be null, interpreted as empty) | ||
52 | * @return the difference of the sets | ||
53 | * @since 1.7 | ||
54 | */ | ||
55 | public static <V> Set<V> difference(Set<V> set1, Set<V> set2) { | ||
56 | if (set1 == null) | ||
57 | return CollectionsFactory.createSet(); | ||
58 | |||
59 | Set<V> difference = CollectionsFactory.createSet(set1); | ||
60 | if (set2 != null) difference.removeAll(set2); | ||
61 | return difference; | ||
62 | } | ||
63 | |||
64 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java new file mode 100644 index 00000000..0e21f323 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java | |||
@@ -0,0 +1,160 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, 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.base.itc.graphimpl; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; | ||
12 | import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
14 | |||
15 | import java.util.HashMap; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | import java.util.function.Function; | ||
19 | |||
20 | /** | ||
21 | * This class contains utility methods to generate dot representations for {@link Graph} instances. | ||
22 | * | ||
23 | * @author Tamas Szabo | ||
24 | * @since 2.3 | ||
25 | */ | ||
26 | public class DotGenerator { | ||
27 | |||
28 | private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" }; | ||
29 | |||
30 | private DotGenerator() { | ||
31 | |||
32 | } | ||
33 | |||
34 | /** | ||
35 | * Generates the dot representation for the given graph. | ||
36 | * | ||
37 | * @param graph | ||
38 | * the graph | ||
39 | * @param colorSCCs | ||
40 | * specifies if the strongly connected components with size greater than shall be colored | ||
41 | * @param nameFunction | ||
42 | * use this function to provide custom names to nodes, null if the default toString shall be used | ||
43 | * @param colorFunction | ||
44 | * use this function to provide custom color to nodes, null if the default white color shall be used | ||
45 | * @param edgeFunction | ||
46 | * use this function to provide custom edge labels, null if no edge label shall be printed | ||
47 | * @return the dot representation as a string | ||
48 | */ | ||
49 | public static <V> String generateDot(final Graph<V> graph, final boolean colorSCCs, | ||
50 | final Function<V, String> nameFunction, final Function<V, String> colorFunction, | ||
51 | final Function<V, Function<V, String>> edgeFunction) { | ||
52 | final Map<V, String> colorMap = new HashMap<V, String>(); | ||
53 | |||
54 | if (colorSCCs) { | ||
55 | final SCCResult<V> result = SCC.computeSCC(graph); | ||
56 | final Set<Set<V>> sccs = result.getSccs(); | ||
57 | |||
58 | int i = 0; | ||
59 | for (final Set<V> scc : sccs) { | ||
60 | if (scc.size() > 1) { | ||
61 | for (final V node : scc) { | ||
62 | final String color = colorMap.get(node); | ||
63 | if (color == null) { | ||
64 | colorMap.put(node, colors[i % colors.length]); | ||
65 | } else { | ||
66 | colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]); | ||
67 | } | ||
68 | } | ||
69 | i++; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | // if a node has no color yet, then make it white | ||
74 | for (final V node : graph.getAllNodes()) { | ||
75 | if (!colorMap.containsKey(node)) { | ||
76 | colorMap.put(node, "white"); | ||
77 | } | ||
78 | } | ||
79 | } else { | ||
80 | for (final V node : graph.getAllNodes()) { | ||
81 | colorMap.put(node, "white"); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | if (colorFunction != null) { | ||
86 | for (final V node : graph.getAllNodes()) { | ||
87 | colorMap.put(node, colorFunction.apply(node)); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | final StringBuilder builder = new StringBuilder(); | ||
92 | builder.append("digraph g {\n"); | ||
93 | |||
94 | for (final V node : graph.getAllNodes()) { | ||
95 | final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node); | ||
96 | builder.append("\"" + nodePresentation + "\""); | ||
97 | builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]"); | ||
98 | builder.append(";\n"); | ||
99 | } | ||
100 | |||
101 | for (final V source : graph.getAllNodes()) { | ||
102 | final IMemoryView<V> targets = graph.getTargetNodes(source); | ||
103 | if (!targets.isEmpty()) { | ||
104 | final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source); | ||
105 | for (final V target : targets.distinctValues()) { | ||
106 | String edgeLabel = null; | ||
107 | if (edgeFunction != null) { | ||
108 | final Function<V, String> v1 = edgeFunction.apply(source); | ||
109 | if (v1 != null) { | ||
110 | edgeLabel = v1.apply(target); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | final String targetPresentation = nameFunction == null ? target.toString() | ||
115 | : nameFunction.apply(target); | ||
116 | |||
117 | builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\""); | ||
118 | if (edgeLabel != null) { | ||
119 | builder.append("[label=\"" + edgeLabel + "\"]"); | ||
120 | } | ||
121 | builder.append(";\n"); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | builder.append("}"); | ||
127 | return builder.toString(); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Generates the dot representation for the given graph. No special pretty printing customization will be applied. | ||
132 | * | ||
133 | * @param graph | ||
134 | * the graph | ||
135 | * @return the dot representation as a string | ||
136 | */ | ||
137 | public static <V> String generateDot(final Graph<V> graph) { | ||
138 | return generateDot(graph, false, null, null, null); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability. | ||
143 | * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because | ||
144 | * grahpviz will treat different nodes as the same if their shortened names are the same. | ||
145 | * | ||
146 | * @param maxLength | ||
147 | * the maximum length of the text that is kept from the toString of the objects in the graph | ||
148 | * @return the shrunk toString value | ||
149 | */ | ||
150 | public static <V> Function<V, String> getNameShortener(final int maxLength) { | ||
151 | return new Function<V, String>() { | ||
152 | @Override | ||
153 | public String apply(final V obj) { | ||
154 | final String value = obj.toString(); | ||
155 | return value.substring(0, Math.min(value.length(), maxLength)); | ||
156 | } | ||
157 | }; | ||
158 | } | ||
159 | |||
160 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java new file mode 100644 index 00000000..4267579c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java | |||
@@ -0,0 +1,185 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.graphimpl; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; | ||
13 | import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; | ||
14 | import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
19 | |||
20 | import java.util.List; | ||
21 | import java.util.Map; | ||
22 | import java.util.Map.Entry; | ||
23 | import java.util.Set; | ||
24 | |||
25 | public class Graph<V> implements IGraphDataSource<V>, IBiDirectionalGraphDataSource<V> { | ||
26 | |||
27 | // source -> target -> count | ||
28 | private IMultiLookup<V, V> outgoingEdges; | ||
29 | // target -> source -> count | ||
30 | private IMultiLookup<V, V> incomingEdges; | ||
31 | |||
32 | private Set<V> nodes; | ||
33 | |||
34 | private List<IGraphObserver<V>> observers; | ||
35 | |||
36 | public Graph() { | ||
37 | outgoingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); | ||
38 | incomingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); | ||
39 | nodes = CollectionsFactory.createSet(); | ||
40 | observers = CollectionsFactory.createObserverList(); | ||
41 | } | ||
42 | |||
43 | public void insertEdge(V source, V target) { | ||
44 | outgoingEdges.addPair(source, target); | ||
45 | incomingEdges.addPair(target, source); | ||
46 | |||
47 | for (IGraphObserver<V> go : observers) { | ||
48 | go.edgeInserted(source, target); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * No-op if trying to delete edge that does not exist | ||
54 | * | ||
55 | * @since 2.0 | ||
56 | * @see #deleteEdgeIfExists(Object, Object) | ||
57 | */ | ||
58 | public void deleteEdgeIfExists(V source, V target) { | ||
59 | boolean containedEdge = outgoingEdges.lookupOrEmpty(source).containsNonZero(target); | ||
60 | if (containedEdge) { | ||
61 | deleteEdgeThatExists(source, target); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * @throws IllegalStateException | ||
67 | * if trying to delete edge that does not exist | ||
68 | * @since 2.0 | ||
69 | * @see #deleteEdgeIfExists(Object, Object) | ||
70 | */ | ||
71 | public void deleteEdgeThatExists(V source, V target) { | ||
72 | outgoingEdges.removePair(source, target); | ||
73 | incomingEdges.removePair(target, source); | ||
74 | for (IGraphObserver<V> go : observers) { | ||
75 | go.edgeDeleted(source, target); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * @deprecated use explicitly {@link #deleteEdgeThatExists(Object, Object)} or | ||
81 | * {@link #deleteEdgeIfExists(Object, Object)} instead. To preserve backwards compatibility, this method | ||
82 | * delegates to the latter. | ||
83 | * | ||
84 | */ | ||
85 | @Deprecated | ||
86 | public void deleteEdge(V source, V target) { | ||
87 | deleteEdgeIfExists(source, target); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * Insert the given node into the graph. | ||
92 | */ | ||
93 | public void insertNode(V node) { | ||
94 | if (nodes.add(node)) { | ||
95 | for (IGraphObserver<V> go : observers) { | ||
96 | go.nodeInserted(node); | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Deletes the given node AND all of the edges going in and out from the node. | ||
103 | */ | ||
104 | public void deleteNode(V node) { | ||
105 | if (nodes.remove(node)) { | ||
106 | IMemoryView<V> incomingView = incomingEdges.lookup(node); | ||
107 | if (incomingView != null) { | ||
108 | Map<V, Integer> incoming = CollectionsFactory.createMap(incomingView.asMap()); | ||
109 | |||
110 | for (Entry<V, Integer> entry : incoming.entrySet()) { | ||
111 | for (int i = 0; i < entry.getValue(); i++) { | ||
112 | deleteEdgeThatExists(entry.getKey(), node); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | IMemoryView<V> outgoingView = outgoingEdges.lookup(node); | ||
118 | if (outgoingView != null) { | ||
119 | Map<V, Integer> outgoing = CollectionsFactory.createMap(outgoingView.asMap()); | ||
120 | |||
121 | for (Entry<V, Integer> entry : outgoing.entrySet()) { | ||
122 | for (int i = 0; i < entry.getValue(); i++) { | ||
123 | deleteEdgeThatExists(node, entry.getKey()); | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | for (IGraphObserver<V> go : observers) { | ||
129 | go.nodeDeleted(node); | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | |||
134 | @Override | ||
135 | public void attachObserver(IGraphObserver<V> go) { | ||
136 | observers.add(go); | ||
137 | } | ||
138 | |||
139 | @Override | ||
140 | public void attachAsFirstObserver(IGraphObserver<V> observer) { | ||
141 | observers.add(0, observer); | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public void detachObserver(IGraphObserver<V> go) { | ||
146 | observers.remove(go); | ||
147 | } | ||
148 | |||
149 | @Override | ||
150 | public Set<V> getAllNodes() { | ||
151 | return nodes; | ||
152 | } | ||
153 | |||
154 | @Override | ||
155 | public IMemoryView<V> getTargetNodes(V source) { | ||
156 | return outgoingEdges.lookupOrEmpty(source); | ||
157 | } | ||
158 | |||
159 | @Override | ||
160 | public IMemoryView<V> getSourceNodes(V target) { | ||
161 | return incomingEdges.lookupOrEmpty(target); | ||
162 | } | ||
163 | |||
164 | @Override | ||
165 | public String toString() { | ||
166 | StringBuilder sb = new StringBuilder(); | ||
167 | sb.append("nodes = "); | ||
168 | for (V n : getAllNodes()) { | ||
169 | sb.append(n.toString()); | ||
170 | sb.append(" "); | ||
171 | } | ||
172 | sb.append(" edges = "); | ||
173 | for (V source : outgoingEdges.distinctKeys()) { | ||
174 | IMemoryView<V> targets = outgoingEdges.lookup(source); | ||
175 | for (V target : targets.distinctValues()) { | ||
176 | int count = targets.getCount(target); | ||
177 | for (int i = 0; i < count; i++) { | ||
178 | sb.append("(" + source + "," + target + ") "); | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | return sb.toString(); | ||
183 | } | ||
184 | |||
185 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java new file mode 100644 index 00000000..64659447 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.igraph; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.IMultiset; | ||
14 | |||
15 | /** | ||
16 | * A bi-directional graph data source supports all operations that an {@link IGraphDataSource} does, but it | ||
17 | * also makes it possible to query the incoming edges of nodes, not only the outgoing edges. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * | ||
21 | * @param <V> the type of the nodes in the graph | ||
22 | */ | ||
23 | public interface IBiDirectionalGraphDataSource<V> extends IGraphDataSource<V> { | ||
24 | |||
25 | /** | ||
26 | * Returns the source nodes for the given target node. | ||
27 | * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. | ||
28 | * | ||
29 | * The method must not return null. | ||
30 | * | ||
31 | * @param target the target node | ||
32 | * @return the multiset of source nodes | ||
33 | * @since 2.0 | ||
34 | */ | ||
35 | public IMemoryView<V> getSourceNodes(V target); | ||
36 | |||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java new file mode 100644 index 00000000..becab0eb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java | |||
@@ -0,0 +1,110 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.igraph; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
18 | |||
19 | /** | ||
20 | * This class can be used to wrap an {@link IGraphDataSource} into an {@link IBiDirectionalGraphDataSource}. This class | ||
21 | * provides support for the retrieval of source nodes for a given target which is not supported by standard | ||
22 | * {@link IGraphDataSource} implementations. | ||
23 | * | ||
24 | * @author Tamas Szabo | ||
25 | * | ||
26 | * @param <V> | ||
27 | * the type parameter of the nodes in the graph data source | ||
28 | */ | ||
29 | public class IBiDirectionalWrapper<V> implements IBiDirectionalGraphDataSource<V>, IGraphObserver<V> { | ||
30 | |||
31 | private IGraphDataSource<V> wrappedDataSource; | ||
32 | // target -> source -> count | ||
33 | private IMultiLookup<V, V> incomingEdges; | ||
34 | |||
35 | public IBiDirectionalWrapper(IGraphDataSource<V> gds) { | ||
36 | this.wrappedDataSource = gds; | ||
37 | |||
38 | this.incomingEdges = CollectionsFactory.createMultiLookup( | ||
39 | Object.class, MemoryType.MULTISETS, Object.class); | ||
40 | |||
41 | if (gds.getAllNodes() != null) { | ||
42 | for (V source : gds.getAllNodes()) { | ||
43 | IMemoryView<V> targets = gds.getTargetNodes(source); | ||
44 | for (V target : targets.distinctValues()) { | ||
45 | int count = targets.getCount(target); | ||
46 | for (int i = 0; i < count; i++) { | ||
47 | edgeInserted(source, target); | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | gds.attachAsFirstObserver(this); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public void attachObserver(IGraphObserver<V> observer) { | ||
58 | wrappedDataSource.attachObserver(observer); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public void attachAsFirstObserver(IGraphObserver<V> observer) { | ||
63 | wrappedDataSource.attachAsFirstObserver(observer); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public void detachObserver(IGraphObserver<V> observer) { | ||
68 | wrappedDataSource.detachObserver(observer); | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Set<V> getAllNodes() { | ||
73 | return wrappedDataSource.getAllNodes(); | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public IMemoryView<V> getTargetNodes(V source) { | ||
78 | return wrappedDataSource.getTargetNodes(source); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public IMemoryView<V> getSourceNodes(V target) { | ||
83 | return incomingEdges.lookupOrEmpty(target); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void edgeInserted(V source, V target) { | ||
88 | incomingEdges.addPair(target, source); | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public void edgeDeleted(V source, V target) { | ||
93 | incomingEdges.removePair(target, source); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public void nodeInserted(V n) { | ||
98 | |||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public void nodeDeleted(V node) { | ||
103 | |||
104 | } | ||
105 | |||
106 | @Override | ||
107 | public String toString() { | ||
108 | return wrappedDataSource.toString(); | ||
109 | } | ||
110 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java new file mode 100644 index 00000000..3fa65936 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java | |||
@@ -0,0 +1,70 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.igraph; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.IMultiset; | ||
14 | |||
15 | import java.util.Set; | ||
16 | |||
17 | /** | ||
18 | * The interface prescribes the set of operations that a graph data source must support. | ||
19 | * <p> Note that the old version of the interface is broken at version 1.6; | ||
20 | * MultiSets are now presented as Maps instead of Lists. | ||
21 | * | ||
22 | * @author Tamas Szabo | ||
23 | * | ||
24 | * @param <V> | ||
25 | * the type of the nodes in the graph | ||
26 | */ | ||
27 | public interface IGraphDataSource<V> { | ||
28 | |||
29 | /** | ||
30 | * Attaches a new graph observer to this graph data source. Observers will be notified in the order they have been registered. | ||
31 | * | ||
32 | * @param observer the graph observer | ||
33 | */ | ||
34 | public void attachObserver(IGraphObserver<V> observer); | ||
35 | |||
36 | /** | ||
37 | * Attaches a new graph observer to this graph data source as the first one. | ||
38 | * In the notification order this observer will be the first one as long as another call to this method happens. | ||
39 | * | ||
40 | * @param observer the graph observer | ||
41 | * @since 1.6 | ||
42 | */ | ||
43 | public void attachAsFirstObserver(IGraphObserver<V> observer); | ||
44 | |||
45 | /** | ||
46 | * Detaches an already registered graph observer from this graph data source. | ||
47 | * | ||
48 | * @param observer the graph observer | ||
49 | */ | ||
50 | public void detachObserver(IGraphObserver<V> observer); | ||
51 | |||
52 | /** | ||
53 | * Returns the complete set of nodes in the graph data source. | ||
54 | * | ||
55 | * @return the set of all nodes | ||
56 | */ | ||
57 | public Set<V> getAllNodes(); | ||
58 | |||
59 | /** | ||
60 | * Returns the target nodes for the given source node. | ||
61 | * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. | ||
62 | * | ||
63 | * The method must not return null. | ||
64 | * | ||
65 | * @param source the source node | ||
66 | * @return the multiset of target nodes | ||
67 | * @since 2.0 | ||
68 | */ | ||
69 | public IMemoryView<V> getTargetNodes(V source); | ||
70 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java new file mode 100644 index 00000000..5cb2d9fa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.igraph; | ||
11 | |||
12 | /** | ||
13 | * Interface GraphObserver is used to observ the changes in a graph; edge and node insertion/deleteion. | ||
14 | * | ||
15 | * @author Tamas Szabo | ||
16 | * | ||
17 | */ | ||
18 | public interface IGraphObserver<V> { | ||
19 | |||
20 | /** | ||
21 | * Used to notify when an edge is inserted into the graph. | ||
22 | * | ||
23 | * @param source | ||
24 | * the source of the edge | ||
25 | * @param target | ||
26 | * the target of the edge | ||
27 | */ | ||
28 | public void edgeInserted(V source, V target); | ||
29 | |||
30 | /** | ||
31 | * Used to notify when an edge is deleted from the graph. | ||
32 | * | ||
33 | * @param source | ||
34 | * the source of the edge | ||
35 | * @param target | ||
36 | * the target of the edge | ||
37 | */ | ||
38 | public void edgeDeleted(V source, V target); | ||
39 | |||
40 | /** | ||
41 | * Used to notify when a node is inserted into the graph. | ||
42 | * | ||
43 | * @param n | ||
44 | * the node | ||
45 | */ | ||
46 | public void nodeInserted(V n); | ||
47 | |||
48 | /** | ||
49 | * Used to notify when a node is deleted from the graph. | ||
50 | * | ||
51 | * @param n | ||
52 | * the node | ||
53 | */ | ||
54 | public void nodeDeleted(V n); | ||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java new file mode 100644 index 00000000..5924b723 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.igraph; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; | ||
15 | |||
16 | /** | ||
17 | * This interface defines those methods that a transitive reachability data source should provide. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * | ||
21 | * @param <V> | ||
22 | * the type parameter of the node | ||
23 | */ | ||
24 | public interface ITcDataSource<V> { | ||
25 | |||
26 | /** | ||
27 | * Attach a transitive closure relation observer. | ||
28 | * | ||
29 | * @param to | ||
30 | * the observer object | ||
31 | */ | ||
32 | public void attachObserver(ITcObserver<V> to); | ||
33 | |||
34 | /** | ||
35 | * Detach a transitive closure relation observer. | ||
36 | * | ||
37 | * @param to | ||
38 | * the observer object | ||
39 | */ | ||
40 | public void detachObserver(ITcObserver<V> to); | ||
41 | |||
42 | /** | ||
43 | * Returns all nodes which are reachable from the source node. | ||
44 | * | ||
45 | * @param source | ||
46 | * the source node | ||
47 | * @return the set of target nodes | ||
48 | */ | ||
49 | public Set<V> getAllReachableTargets(V source); | ||
50 | |||
51 | /** | ||
52 | * Returns all nodes from which the target node is reachable. | ||
53 | * | ||
54 | * @param target | ||
55 | * the target node | ||
56 | * @return the set of source nodes | ||
57 | */ | ||
58 | public Set<V> getAllReachableSources(V target); | ||
59 | |||
60 | /** | ||
61 | * Returns true if the target node is reachable from the source node. | ||
62 | * | ||
63 | * @param source | ||
64 | * the source node | ||
65 | * @param target | ||
66 | * the target node | ||
67 | * @return true if target is reachable from source, false otherwise | ||
68 | */ | ||
69 | public boolean isReachable(V source, V target); | ||
70 | |||
71 | /** | ||
72 | * The returned {@link IGraphPathFinder} can be used to retrieve paths between nodes using transitive reachability. | ||
73 | * | ||
74 | * @return a path finder for the graph. | ||
75 | */ | ||
76 | public IGraphPathFinder<V> getPathFinder(); | ||
77 | |||
78 | /** | ||
79 | * Call this method to properly dispose the data structures of a transitive closure algorithm. | ||
80 | */ | ||
81 | public void dispose(); | ||
82 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java new file mode 100644 index 00000000..fded53f1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.base.itc.igraph; | ||
11 | |||
12 | /** | ||
13 | * Interface ITcObserver is used to observe the changes in a transitive closure relation; tuple insertion/deletion. | ||
14 | * | ||
15 | * @author Szabo Tamas | ||
16 | * | ||
17 | */ | ||
18 | public interface ITcObserver<V> { | ||
19 | |||
20 | /** | ||
21 | * Used to notify when a tuple is inserted into the transitive closure relation. | ||
22 | * | ||
23 | * @param source | ||
24 | * the source of the tuple | ||
25 | * @param target | ||
26 | * the target of the tuple | ||
27 | */ | ||
28 | public void tupleInserted(V source, V target); | ||
29 | |||
30 | /** | ||
31 | * Used to notify when a tuple is deleted from the transitive closure relation. | ||
32 | * | ||
33 | * @param source | ||
34 | * the source of the tuple | ||
35 | * @param target | ||
36 | * the target of the tuple | ||
37 | */ | ||
38 | public void tupleDeleted(V source, V target); | ||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java new file mode 100644 index 00000000..83f6f766 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java | |||
@@ -0,0 +1,42 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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; | ||
10 | |||
11 | /** | ||
12 | * A common base class for all exceptions thrown by various VIATRA Query Runtime APIs. | ||
13 | * | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * @since 2.0 | ||
16 | */ | ||
17 | public abstract class ViatraQueryRuntimeException extends RuntimeException { | ||
18 | |||
19 | private static final long serialVersionUID = -8505253058035069310L; | ||
20 | |||
21 | public ViatraQueryRuntimeException() { | ||
22 | super(); | ||
23 | } | ||
24 | |||
25 | public ViatraQueryRuntimeException(String message) { | ||
26 | super(message); | ||
27 | } | ||
28 | |||
29 | public ViatraQueryRuntimeException(Throwable cause) { | ||
30 | super(cause); | ||
31 | } | ||
32 | |||
33 | public ViatraQueryRuntimeException(String message, Throwable cause) { | ||
34 | super(message, cause); | ||
35 | } | ||
36 | |||
37 | public ViatraQueryRuntimeException(String message, Throwable cause, boolean enableSuppression, | ||
38 | boolean writableStackTrace) { | ||
39 | super(message, cause, enableSuppression, writableStackTrace); | ||
40 | } | ||
41 | |||
42 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java new file mode 100644 index 00000000..2c09ede1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java | |||
@@ -0,0 +1,24 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | /** | ||
12 | * @since 2.0 | ||
13 | */ | ||
14 | class AverageAccumulator<Domain> { | ||
15 | Domain value; | ||
16 | long count; | ||
17 | |||
18 | public AverageAccumulator(Domain value, long count) { | ||
19 | super(); | ||
20 | this.value = value; | ||
21 | this.count = count; | ||
22 | } | ||
23 | |||
24 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java new file mode 100644 index 00000000..e8a26afd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public class DoubleAverageOperator implements IMultisetAggregationOperator<Double, AverageAccumulator<Double>, Double> { | ||
21 | |||
22 | public static final DoubleAverageOperator INSTANCE = new DoubleAverageOperator(); | ||
23 | |||
24 | private DoubleAverageOperator() { | ||
25 | // Singleton, do not call. | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getShortDescription() { | ||
30 | return "avg<Integer> incrementally computes the average of java.lang.Integer values"; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getName() { | ||
35 | return "avg<Integer>"; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public AverageAccumulator<Double> createNeutral() { | ||
40 | return new AverageAccumulator<Double>(0d, 0l); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isNeutral(AverageAccumulator<Double> result) { | ||
45 | return result.count == 0l; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public AverageAccumulator<Double> update(AverageAccumulator<Double> oldResult, Double updateValue, | ||
50 | boolean isInsertion) { | ||
51 | if (isInsertion) { | ||
52 | oldResult.value += updateValue; | ||
53 | oldResult.count++; | ||
54 | } else { | ||
55 | oldResult.value -= updateValue; | ||
56 | oldResult.count--; | ||
57 | } | ||
58 | return oldResult; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Double getAggregate(AverageAccumulator<Double> result) { | ||
63 | return (result.count == 0) | ||
64 | ? null | ||
65 | : result.value/result.count; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Double aggregateStream(Stream<Double> stream) { | ||
70 | final OptionalDouble averageOpt = stream.mapToDouble(Double::doubleValue).average(); | ||
71 | return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.4 | ||
76 | */ | ||
77 | @Override | ||
78 | public AverageAccumulator<Double> clone(AverageAccumulator<Double> original) { | ||
79 | return new AverageAccumulator<Double>(original.value, original.count); | ||
80 | } | ||
81 | |||
82 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java new file mode 100644 index 00000000..744b0cd1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java | |||
@@ -0,0 +1,62 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; | ||
14 | |||
15 | /** | ||
16 | * Incrementally computes the sum of java.lang.Double values | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class DoubleSumOperator extends AbstractMemorylessAggregationOperator<Double, Double> { | ||
21 | public static final DoubleSumOperator INSTANCE = new DoubleSumOperator(); | ||
22 | |||
23 | private DoubleSumOperator() { | ||
24 | // Singleton, do not call. | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public String getShortDescription() { | ||
29 | return "sum<Double> incrementally computes the sum of java.lang.Double values"; | ||
30 | } | ||
31 | @Override | ||
32 | public String getName() { | ||
33 | return "sum<Double>"; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Double createNeutral() { | ||
38 | return 0d; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isNeutral(Double result) { | ||
43 | return createNeutral().equals(result); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Double update(Double oldResult, Double updateValue, boolean isInsertion) { | ||
48 | return isInsertion ? | ||
49 | oldResult + updateValue : | ||
50 | oldResult - updateValue; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | @Override | ||
57 | public Double aggregateStream(Stream<Double> stream) { | ||
58 | return stream.mapToDouble(Double::doubleValue).sum(); | ||
59 | } | ||
60 | |||
61 | |||
62 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java new file mode 100644 index 00000000..ee4ceeb8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java | |||
@@ -0,0 +1,135 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.SortedMap; | ||
13 | import java.util.TreeMap; | ||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
17 | |||
18 | /** | ||
19 | * Incrementally computes the minimum or maximum of java.lang.Comparable values, using the default comparison | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 1.4 | ||
23 | */ | ||
24 | public class ExtremumOperator<T extends Comparable<T>> | ||
25 | implements IMultisetAggregationOperator<T, SortedMap<T, Integer>, T> { | ||
26 | |||
27 | public enum Extreme { | ||
28 | MIN, MAX; | ||
29 | |||
30 | /** | ||
31 | * @since 2.0 | ||
32 | */ | ||
33 | public <T> T pickFrom(SortedMap<T, Integer> nonEmptyMultiSet) { | ||
34 | switch(this) { | ||
35 | case MIN: | ||
36 | return nonEmptyMultiSet.firstKey(); | ||
37 | case MAX: | ||
38 | return nonEmptyMultiSet.lastKey(); | ||
39 | default: | ||
40 | return null; | ||
41 | } | ||
42 | } | ||
43 | } | ||
44 | |||
45 | private static final ExtremumOperator MIN_OP = new ExtremumOperator<>(Extreme.MIN); | ||
46 | private static final ExtremumOperator MAX_OP = new ExtremumOperator<>(Extreme.MAX); | ||
47 | |||
48 | public static <T extends Comparable<T>> ExtremumOperator<T> getMin() { | ||
49 | return MIN_OP; | ||
50 | } | ||
51 | public static <T extends Comparable<T>> ExtremumOperator<T> getMax() { | ||
52 | return MAX_OP; | ||
53 | } | ||
54 | |||
55 | Extreme extreme; | ||
56 | private ExtremumOperator(Extreme extreme) { | ||
57 | super(); | ||
58 | this.extreme = extreme; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public String getShortDescription() { | ||
63 | String opName = getName(); | ||
64 | return String.format( | ||
65 | "%s incrementally computes the %simum of java.lang.Comparable values, using the default comparison", | ||
66 | opName, opName); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public String getName() { | ||
71 | return extreme.name().toLowerCase(); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.0 | ||
76 | */ | ||
77 | @Override | ||
78 | public SortedMap<T, Integer> createNeutral() { | ||
79 | return new TreeMap<>(); | ||
80 | } | ||
81 | |||
82 | /** | ||
83 | * @since 2.0 | ||
84 | */ | ||
85 | @Override | ||
86 | public boolean isNeutral(SortedMap<T, Integer> result) { | ||
87 | return result.isEmpty(); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * @since 2.0 | ||
92 | */ | ||
93 | @Override | ||
94 | public SortedMap<T, Integer> update(SortedMap<T, Integer> oldResult, T updateValue, boolean isInsertion) { | ||
95 | oldResult.compute(updateValue, (value, c) -> { | ||
96 | int count = (c == null) ? 0 : c; | ||
97 | int result = (isInsertion) ? count+1 : count-1; | ||
98 | return (result == 0) ? null : result; | ||
99 | }); | ||
100 | return oldResult; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * @since 2.0 | ||
105 | */ | ||
106 | @Override | ||
107 | public T getAggregate(SortedMap<T, Integer> result) { | ||
108 | return result.isEmpty() ? null : | ||
109 | extreme.pickFrom(result); | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | @Override | ||
116 | public T aggregateStream(Stream<T> stream) { | ||
117 | switch (extreme) { | ||
118 | case MIN: | ||
119 | return stream.min(Comparator.naturalOrder()).orElse(null); | ||
120 | case MAX: | ||
121 | return stream.max(Comparator.naturalOrder()).orElse(null); | ||
122 | default: | ||
123 | return null; | ||
124 | } | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * @since 2.4 | ||
129 | */ | ||
130 | @Override | ||
131 | public SortedMap<T, Integer> clone(SortedMap<T, Integer> original) { | ||
132 | return new TreeMap<T, Integer>(original); | ||
133 | } | ||
134 | |||
135 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java new file mode 100644 index 00000000..bf422476 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public class IntegerAverageOperator implements IMultisetAggregationOperator<Integer, AverageAccumulator<Integer>, Double> { | ||
21 | |||
22 | public static final IntegerAverageOperator INSTANCE = new IntegerAverageOperator(); | ||
23 | |||
24 | private IntegerAverageOperator() { | ||
25 | // Singleton, do not call. | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getShortDescription() { | ||
30 | return "avg<Integer> incrementally computes the average of java.lang.Integer values"; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getName() { | ||
35 | return "avg<Integer>"; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public AverageAccumulator<Integer> createNeutral() { | ||
40 | return new AverageAccumulator<Integer>(0, 0l); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isNeutral(AverageAccumulator<Integer> result) { | ||
45 | return result.count == 0l; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public AverageAccumulator<Integer> update(AverageAccumulator<Integer> oldResult, Integer updateValue, | ||
50 | boolean isInsertion) { | ||
51 | if (isInsertion) { | ||
52 | oldResult.value += updateValue; | ||
53 | oldResult.count++; | ||
54 | } else { | ||
55 | oldResult.value -= updateValue; | ||
56 | oldResult.count--; | ||
57 | } | ||
58 | return oldResult; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Double getAggregate(AverageAccumulator<Integer> result) { | ||
63 | return (result.count == 0) | ||
64 | ? null | ||
65 | : ((double)result.value)/result.count; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Double aggregateStream(Stream<Integer> stream) { | ||
70 | final OptionalDouble averageOpt = stream.mapToInt(Integer::intValue).average(); | ||
71 | return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.4 | ||
76 | */ | ||
77 | @Override | ||
78 | public AverageAccumulator<Integer> clone(AverageAccumulator<Integer> original) { | ||
79 | return new AverageAccumulator<Integer>(original.value, original.count); | ||
80 | } | ||
81 | |||
82 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java new file mode 100644 index 00000000..18584256 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; | ||
14 | |||
15 | /** | ||
16 | * Incrementally computes the sum of java.lang.Integer values | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class IntegerSumOperator extends AbstractMemorylessAggregationOperator<Integer, Integer> { | ||
21 | public static final IntegerSumOperator INSTANCE = new IntegerSumOperator(); | ||
22 | |||
23 | private IntegerSumOperator() { | ||
24 | // Singleton, do not call. | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public String getShortDescription() { | ||
29 | return "sum<Integer> incrementally computes the sum of java.lang.Integer values"; | ||
30 | } | ||
31 | @Override | ||
32 | public String getName() { | ||
33 | return "sum<Integer>"; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Integer createNeutral() { | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isNeutral(Integer result) { | ||
43 | return createNeutral().equals(result); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Integer update(Integer oldResult, Integer updateValue, boolean isInsertion) { | ||
48 | return isInsertion ? | ||
49 | oldResult + updateValue : | ||
50 | oldResult - updateValue; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | @Override | ||
57 | public Integer aggregateStream(Stream<Integer> stream) { | ||
58 | return stream.mapToInt(Integer::intValue).sum(); | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java new file mode 100644 index 00000000..d56c9507 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java | |||
@@ -0,0 +1,82 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.util.OptionalDouble; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
15 | |||
16 | /** | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public class LongAverageOperator implements IMultisetAggregationOperator<Long, AverageAccumulator<Long>, Double> { | ||
21 | |||
22 | public static final LongAverageOperator INSTANCE = new LongAverageOperator(); | ||
23 | |||
24 | private LongAverageOperator() { | ||
25 | // Singleton, do not call. | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String getShortDescription() { | ||
30 | return "avg<Integer> incrementally computes the average of java.lang.Integer values"; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public String getName() { | ||
35 | return "avg<Integer>"; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public AverageAccumulator<Long> createNeutral() { | ||
40 | return new AverageAccumulator<Long>(0l, 0l); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean isNeutral(AverageAccumulator<Long> result) { | ||
45 | return result.count == 0l; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public AverageAccumulator<Long> update(AverageAccumulator<Long> oldResult, Long updateValue, | ||
50 | boolean isInsertion) { | ||
51 | if (isInsertion) { | ||
52 | oldResult.value += updateValue; | ||
53 | oldResult.count++; | ||
54 | } else { | ||
55 | oldResult.value -= updateValue; | ||
56 | oldResult.count--; | ||
57 | } | ||
58 | return oldResult; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Double getAggregate(AverageAccumulator<Long> result) { | ||
63 | return (result.count == 0) | ||
64 | ? null | ||
65 | : ((double)result.value)/result.count; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Double aggregateStream(Stream<Long> stream) { | ||
70 | final OptionalDouble averageOpt = stream.mapToLong(Long::longValue).average(); | ||
71 | return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 2.4 | ||
76 | */ | ||
77 | @Override | ||
78 | public AverageAccumulator<Long> clone(AverageAccumulator<Long> original) { | ||
79 | return new AverageAccumulator<Long>(original.value, original.count); | ||
80 | } | ||
81 | |||
82 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java new file mode 100644 index 00000000..29ded090 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; | ||
14 | |||
15 | /** | ||
16 | * Incrementally computes the sum of java.lang.Long values | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class LongSumOperator extends AbstractMemorylessAggregationOperator<Long, Long> { | ||
21 | public static final LongSumOperator INSTANCE = new LongSumOperator(); | ||
22 | |||
23 | private LongSumOperator() { | ||
24 | // Singleton, do not call. | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public String getShortDescription() { | ||
29 | return "sum<Long> incrementally computes the sum of java.lang.Long values"; | ||
30 | } | ||
31 | @Override | ||
32 | public String getName() { | ||
33 | return "sum<Long>"; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Long createNeutral() { | ||
38 | return 0L; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public boolean isNeutral(Long result) { | ||
43 | return createNeutral().equals(result); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Long update(Long oldResult, Long updateValue, boolean isInsertion) { | ||
48 | return isInsertion ? | ||
49 | oldResult + updateValue : | ||
50 | oldResult - updateValue; | ||
51 | } | ||
52 | |||
53 | /** | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | @Override | ||
57 | public Long aggregateStream(Stream<Long> stream) { | ||
58 | return stream.mapToLong(Long::longValue).sum(); | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java new file mode 100644 index 00000000..c25678aa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
14 | |||
15 | /** | ||
16 | * This aggregator calculates the average of the values of a selected aggregate parameter of a called pattern. The aggregate | ||
17 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
18 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
19 | * summation. | ||
20 | * | ||
21 | * @since 2.0 | ||
22 | * | ||
23 | */ | ||
24 | @AggregatorType( | ||
25 | parameterTypes = {Integer.class, Double.class, Long.class}, | ||
26 | returnTypes = {Double.class, Double.class, Double.class}) | ||
27 | public final class avg implements IAggregatorFactory { | ||
28 | |||
29 | @Override | ||
30 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
31 | if (Integer.class.equals(domainClass)) | ||
32 | return new BoundAggregator(IntegerAverageOperator.INSTANCE, Integer.class, Double.class); | ||
33 | if (Double.class.equals(domainClass)) | ||
34 | return new BoundAggregator(DoubleAverageOperator.INSTANCE, Double.class, Double.class); | ||
35 | if (Long.class.equals(domainClass)) | ||
36 | return new BoundAggregator(LongAverageOperator.INSTANCE, Long.class, Double.class); | ||
37 | else throw new IllegalArgumentException(); | ||
38 | } | ||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java new file mode 100644 index 00000000..8310a0ce --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
14 | |||
15 | /** | ||
16 | * An aggregator to count the number of matches a pattern has. The return of the aggregator is an non-negative integer | ||
17 | * number. | ||
18 | * | ||
19 | * @since 1.4 | ||
20 | * | ||
21 | */ | ||
22 | @AggregatorType(parameterTypes = {Void.class}, returnTypes = {Integer.class}) | ||
23 | public final class count implements IAggregatorFactory { | ||
24 | |||
25 | @Override | ||
26 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
27 | if (Void.class.equals(domainClass)) | ||
28 | return new BoundAggregator(null, Void.class, Integer.class); | ||
29 | else throw new IllegalArgumentException(); | ||
30 | } | ||
31 | |||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java new file mode 100644 index 00000000..e0236223 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.math.BigDecimal; | ||
12 | import java.math.BigInteger; | ||
13 | import java.util.Calendar; | ||
14 | import java.util.Date; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
19 | |||
20 | /** | ||
21 | * This aggregator calculates the maximum value of a selected aggregate parameter of a called pattern. The aggregate | ||
22 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
23 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
24 | * minimum calculation. | ||
25 | * | ||
26 | * @since 1.4 | ||
27 | * @author Gabor Bergmann | ||
28 | */ | ||
29 | @AggregatorType( | ||
30 | // TODO T extends Comparable? | ||
31 | parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
32 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, | ||
33 | returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
34 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) | ||
35 | public final class max implements IAggregatorFactory { | ||
36 | |||
37 | @Override | ||
38 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
39 | if (Comparable.class.isAssignableFrom(domainClass)) | ||
40 | return new BoundAggregator(ExtremumOperator.getMax(), domainClass, domainClass); | ||
41 | else throw new IllegalArgumentException(); | ||
42 | } | ||
43 | |||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java new file mode 100644 index 00000000..6408c57b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import java.math.BigDecimal; | ||
12 | import java.math.BigInteger; | ||
13 | import java.util.Calendar; | ||
14 | import java.util.Date; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
19 | |||
20 | /** | ||
21 | * This aggregator calculates the minimum value of a selected aggregate parameter of a called pattern. The aggregate | ||
22 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
23 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
24 | * minimum calculation. | ||
25 | * | ||
26 | * @since 1.4 | ||
27 | * | ||
28 | */ | ||
29 | @AggregatorType( | ||
30 | // TODO T extends Comparable? | ||
31 | parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
32 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, | ||
33 | returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, | ||
34 | Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) | ||
35 | public final class min implements IAggregatorFactory { | ||
36 | |||
37 | @Override | ||
38 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
39 | if (Comparable.class.isAssignableFrom(domainClass)) | ||
40 | return new BoundAggregator(ExtremumOperator.getMin(), domainClass, domainClass); | ||
41 | else throw new IllegalArgumentException(); | ||
42 | } | ||
43 | |||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java new file mode 100644 index 00000000..69ff2e75 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregators; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; | ||
14 | |||
15 | /** | ||
16 | * This aggregator calculates the sum of the values of a selected aggregate parameter of a called pattern. The aggregate | ||
17 | * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The | ||
18 | * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the | ||
19 | * summation. | ||
20 | * | ||
21 | * @since 1.4 | ||
22 | * | ||
23 | */ | ||
24 | @AggregatorType( | ||
25 | parameterTypes = {Integer.class, Double.class, Long.class}, | ||
26 | returnTypes = {Integer.class, Double.class, Long.class}) | ||
27 | public final class sum implements IAggregatorFactory { | ||
28 | |||
29 | @Override | ||
30 | public BoundAggregator getAggregatorLogic(Class<?> domainClass) { | ||
31 | if (Integer.class.equals(domainClass)) | ||
32 | return new BoundAggregator(IntegerSumOperator.INSTANCE, Integer.class, Integer.class); | ||
33 | if (Double.class.equals(domainClass)) | ||
34 | return new BoundAggregator(DoubleSumOperator.INSTANCE, Double.class, Double.class); | ||
35 | if (Long.class.equals(domainClass)) | ||
36 | return new BoundAggregator(LongSumOperator.INSTANCE, Long.class, Long.class); | ||
37 | else throw new IllegalArgumentException(); | ||
38 | } | ||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java new file mode 100644 index 00000000..1917e909 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java | |||
@@ -0,0 +1,83 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.algorithms; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 2.1 | ||
18 | * | ||
19 | */ | ||
20 | public class OrderedIterableMerge { | ||
21 | |||
22 | private OrderedIterableMerge() { | ||
23 | // Hidden utility class constructor | ||
24 | } | ||
25 | |||
26 | /** | ||
27 | * Lazily merges two iterables, each ordered according to a given comparator. | ||
28 | * Retains order in the result, and also eliminates any duplicates that appear in both arguments. | ||
29 | */ | ||
30 | public static <T> Iterable<T> mergeUniques(Iterable<T> first, Iterable<T> second, Comparator<T> comparator) { | ||
31 | return () -> new Iterator<T>() { | ||
32 | Iterator<T> firstIterator = first.iterator(); | ||
33 | Iterator<T> secondIterator = second.iterator(); | ||
34 | T firstItem; | ||
35 | T secondItem; | ||
36 | |||
37 | { | ||
38 | fetchFirst(); | ||
39 | fetchSecond(); | ||
40 | } | ||
41 | |||
42 | private T fetchFirst() { | ||
43 | T previous = firstItem; | ||
44 | if (firstIterator.hasNext()) | ||
45 | firstItem = firstIterator.next(); | ||
46 | else | ||
47 | firstItem = null; | ||
48 | return previous; | ||
49 | } | ||
50 | private T fetchSecond() { | ||
51 | T previous = secondItem; | ||
52 | if (secondIterator.hasNext()) | ||
53 | secondItem = secondIterator.next(); | ||
54 | else | ||
55 | secondItem = null; | ||
56 | return previous; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public boolean hasNext() { | ||
61 | return firstItem != null || secondItem != null; | ||
62 | } | ||
63 | @Override | ||
64 | public T next() { | ||
65 | if (!hasNext()) throw new NoSuchElementException(); | ||
66 | if (firstItem != null && secondItem != null) { | ||
67 | if (secondItem == firstItem) { // duplicates | ||
68 | fetchFirst(); | ||
69 | return fetchSecond(); | ||
70 | } else if (comparator.compare(firstItem, secondItem) < 0) { | ||
71 | return fetchFirst(); | ||
72 | } else { | ||
73 | return fetchSecond(); | ||
74 | } | ||
75 | } else if (firstItem != null) { | ||
76 | return fetchFirst(); | ||
77 | } else { // secondItem must be non-null | ||
78 | return fetchSecond(); | ||
79 | } | ||
80 | } | ||
81 | }; | ||
82 | } | ||
83 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java new file mode 100644 index 00000000..c69f08e5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java | |||
@@ -0,0 +1,214 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.algorithms; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Iterator; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | |||
20 | /** | ||
21 | * Union-find data structure implementation. Note that the implementation relies on the correct implementation of the | ||
22 | * equals method of the type parameter's class. | ||
23 | * | ||
24 | * @author Tamas Szabo | ||
25 | * | ||
26 | * @param <V> | ||
27 | * the type parameter of the element's stored in the union-find data structure | ||
28 | */ | ||
29 | public class UnionFind<V> { | ||
30 | |||
31 | private final Map<V, UnionFindNodeProperty<V>> nodeMap; | ||
32 | final Map<V, Set<V>> setMap; | ||
33 | |||
34 | /** | ||
35 | * Instantiate a new union-find data structure. | ||
36 | */ | ||
37 | public UnionFind() { | ||
38 | nodeMap = CollectionsFactory.createMap(); | ||
39 | setMap = CollectionsFactory.createMap(); | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * Instantiate a new union-find data structure with the given elements as separate sets. | ||
44 | */ | ||
45 | public UnionFind(Iterable<V> elements) { | ||
46 | this(); | ||
47 | for (V element : elements) { | ||
48 | makeSet(element); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * Creates a new union set from a collection of elements. | ||
54 | * | ||
55 | * @param nodes | ||
56 | * the collection of elements | ||
57 | * @return the root element | ||
58 | */ | ||
59 | public V makeSet(Collection<V> nodes) { | ||
60 | if (!nodes.isEmpty()) { | ||
61 | Iterator<V> iterator = nodes.iterator(); | ||
62 | V root = makeSet(iterator.next()); | ||
63 | while (iterator.hasNext()) { | ||
64 | root = union(root, iterator.next()); | ||
65 | } | ||
66 | return root; | ||
67 | } else { | ||
68 | return null; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * This method creates a single set containing the given node. | ||
74 | * | ||
75 | * @param node | ||
76 | * the root node of the set | ||
77 | * @return the root element | ||
78 | */ | ||
79 | public V makeSet(V node) { | ||
80 | if (!nodeMap.containsKey(node)) { | ||
81 | UnionFindNodeProperty<V> prop = new UnionFindNodeProperty<V>(0, node); | ||
82 | nodeMap.put(node, prop); | ||
83 | Set<V> set = new HashSet<V>(); | ||
84 | set.add(node); | ||
85 | setMap.put(node, set); | ||
86 | } | ||
87 | return node; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * Find method with path compression. | ||
92 | * | ||
93 | * @param node | ||
94 | * the node to find | ||
95 | * @return the root node of the set in which the given node can be found | ||
96 | */ | ||
97 | public V find(V node) { | ||
98 | UnionFindNodeProperty<V> prop = nodeMap.get(node); | ||
99 | |||
100 | if (prop != null) { | ||
101 | if (prop.parent.equals(node)) { | ||
102 | return node; | ||
103 | } else { | ||
104 | prop.parent = find(prop.parent); | ||
105 | return prop.parent; | ||
106 | } | ||
107 | } | ||
108 | return null; | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Union by rank implementation of the two sets which contain x and y; x and/or y can be a single element from the | ||
113 | * universe. | ||
114 | * | ||
115 | * @param x | ||
116 | * set or single element of the universe | ||
117 | * @param y | ||
118 | * set or single element of the universe | ||
119 | * @return the new root of the two sets | ||
120 | */ | ||
121 | public V union(V x, V y) { | ||
122 | V xRoot = find(x); | ||
123 | V yRoot = find(y); | ||
124 | |||
125 | if ((xRoot == null) || (yRoot == null)) { | ||
126 | return union( (xRoot == null) ? makeSet(x) : xRoot, (yRoot == null) ? makeSet(y) : yRoot); | ||
127 | } | ||
128 | else if (!xRoot.equals(yRoot)) { | ||
129 | UnionFindNodeProperty<V> xRootProp = nodeMap.get(xRoot); | ||
130 | UnionFindNodeProperty<V> yRootProp = nodeMap.get(yRoot); | ||
131 | |||
132 | if (xRootProp.rank < yRootProp.rank) { | ||
133 | xRootProp.parent = yRoot; | ||
134 | setMap.get(yRoot).addAll(setMap.get(xRoot)); | ||
135 | setMap.remove(xRoot); | ||
136 | return yRoot; | ||
137 | } else {// (xRootProp.rank >= yRootProp.rank) | ||
138 | yRootProp.parent = xRoot; | ||
139 | yRootProp.rank = (xRootProp.rank == yRootProp.rank) ? yRootProp.rank + 1 : yRootProp.rank; | ||
140 | setMap.get(xRoot).addAll(setMap.get(yRoot)); | ||
141 | setMap.remove(yRoot); | ||
142 | return xRoot; | ||
143 | } | ||
144 | } else { | ||
145 | return xRoot; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * Places the given elements in to the same partition. | ||
151 | */ | ||
152 | public void unite(Set<V> elements) { | ||
153 | if (elements.size() > 1) { | ||
154 | V current = null; | ||
155 | for (V element : elements) { | ||
156 | if (current != null) { | ||
157 | if (getPartition(element) != null) { | ||
158 | union(current, element); | ||
159 | } | ||
160 | } else { | ||
161 | if (getPartition(element) != null) { | ||
162 | current = element; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * Delete the set whose root is the given node. | ||
171 | * | ||
172 | * @param root | ||
173 | * the root node | ||
174 | */ | ||
175 | public void deleteSet(V root) { | ||
176 | // if (setMap.containsKey(root)) | ||
177 | for (V n : setMap.get(root)) { | ||
178 | nodeMap.remove(n); | ||
179 | } | ||
180 | setMap.remove(root); | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * Returns if all given elements are in the same partition. | ||
185 | */ | ||
186 | public boolean isSameUnion(Set<V> elements) { | ||
187 | for (Set<V> partition : setMap.values()) { | ||
188 | if (partition.containsAll(elements)) { | ||
189 | return true; | ||
190 | } | ||
191 | } | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | |||
196 | /** | ||
197 | * Returns the partition in which the given element can be found, or null otherwise. | ||
198 | */ | ||
199 | public Set<V> getPartition(V element) { | ||
200 | V root = find(element); | ||
201 | return setMap.get(root); | ||
202 | } | ||
203 | |||
204 | /** | ||
205 | * Returns all partitions. | ||
206 | */ | ||
207 | public Collection<Set<V>> getPartitions() { | ||
208 | return setMap.values(); | ||
209 | } | ||
210 | |||
211 | public Set<V> getPartitionHeads() { | ||
212 | return setMap.keySet(); | ||
213 | } | ||
214 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java new file mode 100644 index 00000000..82852f9c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.algorithms; | ||
11 | |||
12 | public class UnionFindNodeProperty<V> { | ||
13 | |||
14 | public int rank; | ||
15 | public V parent; | ||
16 | |||
17 | public UnionFindNodeProperty() { | ||
18 | this.rank = 0; | ||
19 | this.parent = null; | ||
20 | } | ||
21 | |||
22 | public UnionFindNodeProperty(int rank, V parent) { | ||
23 | super(); | ||
24 | this.rank = rank; | ||
25 | this.parent = parent; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String toString() { | ||
30 | return "[rank:" + rank + ", parent:" + parent.toString() + "]"; | ||
31 | } | ||
32 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java new file mode 100644 index 00000000..317293bf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NopTraceCollector; | ||
13 | |||
14 | /** | ||
15 | * Query evaluation hints applicable to any engine | ||
16 | * @since 1.6 | ||
17 | * | ||
18 | */ | ||
19 | public final class CommonQueryHintOptions { | ||
20 | |||
21 | private CommonQueryHintOptions() { | ||
22 | // Hiding constructor for utility class | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * This hint instructs the query backends to record trace information into the given trace collector | ||
27 | */ | ||
28 | public static final QueryHintOption<IRewriterTraceCollector> normalizationTraceCollector = | ||
29 | hintOption("normalizationTraceCollector", NopTraceCollector.INSTANCE); | ||
30 | |||
31 | // internal helper for conciseness | ||
32 | private static <T> QueryHintOption<T> hintOption(String hintKeyLocalName, T defaultValue) { | ||
33 | return new QueryHintOption<>(CommonQueryHintOptions.class, hintKeyLocalName, defaultValue); | ||
34 | } | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java new file mode 100644 index 00000000..40853f46 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java | |||
@@ -0,0 +1,89 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | |||
15 | /** | ||
16 | * Function object that specifies how hints (including backend preferences) shall propagate through pattern calls. | ||
17 | * | ||
18 | * <p> A few useful default implementations are included as static fields. | ||
19 | * | ||
20 | * <p> As of 2.1, only suppported by the local search backend, and only if the pattern call is not flattened. | ||
21 | * | ||
22 | * @author Gabor Bergmann | ||
23 | * @since 2.1 | ||
24 | */ | ||
25 | @FunctionalInterface | ||
26 | public interface ICallDelegationStrategy { | ||
27 | |||
28 | /** | ||
29 | * Specifies how hints (including backend preferences) shall propagate through pattern calls. | ||
30 | * | ||
31 | * @param call a {@link PConstraint} in a query that calls another query. | ||
32 | * @param callerHint a hint under which the calling pattern is evaluated, | ||
33 | * @param callerBackend the actual backend evaluating the calling pattern. | ||
34 | * @param calleeHintProvider the provider of hints for the called pattern. | ||
35 | * @return the hints, including backend selection, | ||
36 | * that the backend responsible for the caller pattern must specify when | ||
37 | * requesting the {@link IQueryResultProvider} for the called pattern via {@link IQueryResultProviderAccess}. | ||
38 | */ | ||
39 | public QueryEvaluationHint transformHints(IQueryReference call, | ||
40 | QueryEvaluationHint callerHint, | ||
41 | IQueryBackend callerBackend, | ||
42 | IQueryBackendHintProvider calleeHintProvider); | ||
43 | |||
44 | |||
45 | /** | ||
46 | * Options known for callee are used to override caller options, except the backend selection. | ||
47 | * Always use the same backend for the callee and the caller, regardless what is specified for the callee pattern. | ||
48 | * @author Gabor Bergmann | ||
49 | */ | ||
50 | public static final ICallDelegationStrategy FULL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { | ||
51 | QueryEvaluationHint calleeHint = | ||
52 | calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); | ||
53 | QueryEvaluationHint result = | ||
54 | callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); | ||
55 | |||
56 | QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( | ||
57 | null /* settings-ignorant */, callerBackend.getFactory()); | ||
58 | result = result.overrideBy(backendAdhesion); | ||
59 | return result; | ||
60 | }; | ||
61 | /** | ||
62 | * Options known for callee are used to override caller options, including the backend selection. | ||
63 | * If callee does not specify a backend requirement, the backend of the caller is kept. | ||
64 | * @author Gabor Bergmann | ||
65 | */ | ||
66 | public static final ICallDelegationStrategy PARTIAL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { | ||
67 | QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( | ||
68 | null /* settings-ignorant */, callerBackend.getFactory()); | ||
69 | |||
70 | QueryEvaluationHint result = | ||
71 | callerHint == null ? backendAdhesion : callerHint.overrideBy(backendAdhesion); | ||
72 | |||
73 | QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); | ||
74 | result = result.overrideBy(calleeHint); | ||
75 | |||
76 | return result; | ||
77 | }; | ||
78 | /** | ||
79 | * Options known for callee are used to override caller options, including the backend selection. | ||
80 | * Always use the backend specified for the callee (or the default if none), regardless of the backend of the caller. | ||
81 | * @author Gabor Bergmann | ||
82 | */ | ||
83 | public static final ICallDelegationStrategy NO_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { | ||
84 | QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); | ||
85 | return callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); | ||
86 | }; | ||
87 | |||
88 | |||
89 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java new file mode 100644 index 00000000..104b68a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. | ||
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.backend; | ||
10 | |||
11 | /** | ||
12 | * Implementations of this interface can be used to decide whether a matcher created by an arbitrary backend can | ||
13 | * potentially be used as a substitute for another matcher. | ||
14 | * | ||
15 | * @author Grill Balázs | ||
16 | * @since 1.4 | ||
17 | * | ||
18 | */ | ||
19 | public interface IMatcherCapability { | ||
20 | |||
21 | /** | ||
22 | * Returns true if matchers of this capability can be used as a substitute for a matcher implementing the given capability | ||
23 | */ | ||
24 | public boolean canBeSubstitute(IMatcherCapability capability); | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java new file mode 100644 index 00000000..c85f10a4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * Internal interface for a VIATRA query specification. Each query is associated with a pattern. Methods instantiate a matcher | ||
16 | * of the pattern with various parameters. | ||
17 | * | ||
18 | * @author Bergmann Gábor | ||
19 | * @since 0.9 | ||
20 | * @noextend This interface is not intended to be extended by users of the VIATRA framework, and should only be used by the query engine | ||
21 | */ | ||
22 | public interface IQueryBackend { | ||
23 | |||
24 | /** | ||
25 | * @return true iff this backend is incremental, i.e. it caches the results of queries for quick retrieval, | ||
26 | * and can provide update notifications on result set changes. | ||
27 | */ | ||
28 | public boolean isCaching(); | ||
29 | |||
30 | /** | ||
31 | * Returns a result provider for a given query. Repeated calls may return the same instance. | ||
32 | * @throws ViatraQueryRuntimeException | ||
33 | */ | ||
34 | public IQueryResultProvider getResultProvider(PQuery query); | ||
35 | |||
36 | /** | ||
37 | * Returns a result provider for a given query. Repeated calls may return the same instance. | ||
38 | * @param optional hints that may override engine and query defaults (as provided by {@link IQueryBackendHintProvider}). Can be null. | ||
39 | * @throws ViatraQueryRuntimeException | ||
40 | * @since 1.4 | ||
41 | */ | ||
42 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints); | ||
43 | |||
44 | /** | ||
45 | * Returns an existing result provider for a given query, if it was previously constructed, returns null otherwise. | ||
46 | * Will not construct and initialize new result providers. | ||
47 | */ | ||
48 | public IQueryResultProvider peekExistingResultProvider(PQuery query); | ||
49 | |||
50 | /** | ||
51 | * Propagates all pending updates in this query backend. The implementation of this method is optional, and it | ||
52 | * can be ignored entirely if the backend does not delay updates. | ||
53 | * @since 1.6 | ||
54 | */ | ||
55 | public void flushUpdates(); | ||
56 | |||
57 | /** | ||
58 | * Disposes the query backend. | ||
59 | */ | ||
60 | public abstract void dispose(); | ||
61 | |||
62 | /** | ||
63 | * @return the factory that created this backend, if this functionality is supported | ||
64 | * @since 2.1 | ||
65 | */ | ||
66 | public IQueryBackendFactory getFactory(); | ||
67 | |||
68 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java new file mode 100644 index 00000000..e264ab3f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java | |||
@@ -0,0 +1,52 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * A Query Backend Factory identifies a query evaluator implementation, and can create an evaluator instance (an {@link IQueryBackend}) tied to a specific VIATRA Query engine upon request. | ||
16 | * | ||
17 | * <p> The factory is used as a lookup key for the backend instance, | ||
18 | * therefore implementors should either be singletons, or implement equals() / hashCode() accordingly. | ||
19 | * | ||
20 | * @author Bergmann Gabor | ||
21 | * | ||
22 | */ | ||
23 | public interface IQueryBackendFactory { | ||
24 | |||
25 | /** | ||
26 | * Creates a new {@link IQueryBackend} instance tied to the given context elements. | ||
27 | * | ||
28 | * @return an instance of the class returned by {@link #getBackendClass()} that operates in the given context. | ||
29 | * @since 1.5 | ||
30 | */ | ||
31 | public IQueryBackend | ||
32 | create(IQueryBackendContext context); | ||
33 | |||
34 | |||
35 | /** | ||
36 | * The backend instances created by this factory are guaranteed to conform to the returned class. | ||
37 | */ | ||
38 | public Class<? extends IQueryBackend> getBackendClass(); | ||
39 | |||
40 | /** | ||
41 | * Calculate the required capabilities, which are needed to execute the given pattern | ||
42 | * | ||
43 | * @since 1.4 | ||
44 | */ | ||
45 | public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint); | ||
46 | |||
47 | /** | ||
48 | * Returns whether the current backend is caching | ||
49 | * @since 2.0 | ||
50 | */ | ||
51 | public boolean isCaching(); | ||
52 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java new file mode 100644 index 00000000..8787814e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.backend; | ||
10 | |||
11 | /** | ||
12 | * A provider interface for {@link IQueryBackendFactory} instances. | ||
13 | * @since 2.0 | ||
14 | */ | ||
15 | public interface IQueryBackendFactoryProvider { | ||
16 | |||
17 | /** | ||
18 | * Returns a query backend factory instance. The method should return the same instance in case of repeated calls. | ||
19 | */ | ||
20 | IQueryBackendFactory getFactory(); | ||
21 | |||
22 | /** | ||
23 | * Returns whether the given query backend should be considered as system default. If multiple backends are | ||
24 | * registered as system default, it is undefined which one will be chosen. | ||
25 | */ | ||
26 | default boolean isSystemDefaultEngine() { | ||
27 | return false; | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Returns whether the given query backend should be considered as system default search backend. If multiple | ||
32 | * backends are registered as system default, it is undefined which one will be chosen. | ||
33 | */ | ||
34 | default boolean isSystemDefaultSearchBackend() { | ||
35 | return false; | ||
36 | } | ||
37 | |||
38 | |||
39 | /** | ||
40 | * Returns whether the given query backend should be considered as system default caching backend. If multiple | ||
41 | * backends are registered as system default, it is undefined which one will be chosen. | ||
42 | */ | ||
43 | default boolean isSystemDefaultCachingBackend() { | ||
44 | return false; | ||
45 | } | ||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java new file mode 100644 index 00000000..9bb76349 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
12 | |||
13 | /** | ||
14 | * Provides query evaluation hints consisting of the Engine default hints and | ||
15 | * the hints provided by the pattern itself. | ||
16 | * | ||
17 | * @author Bergmann Gabor | ||
18 | * @since 0.9 | ||
19 | * @noimplement This interface is not intended to be implemented by clients, except in the tools.refinery.viatra.runtime plugin. | ||
20 | */ | ||
21 | public interface IQueryBackendHintProvider { | ||
22 | |||
23 | /** | ||
24 | * Suggests query evaluation hints regarding a query. The returned hints reflects the default hints of the | ||
25 | * query engine merged with the hints provided by the pattern itself. These can be overridden via specific | ||
26 | * advanced API of the engine. | ||
27 | * | ||
28 | * @since 1.4 | ||
29 | */ | ||
30 | QueryEvaluationHint getQueryEvaluationHint(PQuery query); | ||
31 | |||
32 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java new file mode 100644 index 00000000..cd7d050f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java | |||
@@ -0,0 +1,202 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.backend; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
19 | |||
20 | /** | ||
21 | * An internal interface of the query backend that provides results of a given query. | ||
22 | * @author Bergmann Gabor | ||
23 | * @noimplement This interface is not intended to be implemented by clients. | ||
24 | */ | ||
25 | public interface IQueryResultProvider { | ||
26 | |||
27 | /** | ||
28 | * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. | ||
29 | * | ||
30 | * @param parameters | ||
31 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
32 | * @pre size of input array must be equal to the number of parameters. | ||
33 | * @since 2.0 | ||
34 | */ | ||
35 | public boolean hasMatch(Object[] parameters); | ||
36 | |||
37 | /** | ||
38 | * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. | ||
39 | * | ||
40 | * @param parameterSeedMask | ||
41 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
42 | * bound to a fixed value | ||
43 | * @param parameters | ||
44 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
45 | * parameterSeedMask, so that for each considered match tuple, | ||
46 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
47 | * @since 2.0 | ||
48 | */ | ||
49 | public boolean hasMatch(TupleMask parameterSeedMask, ITuple projectedParameterSeed); | ||
50 | |||
51 | /** | ||
52 | * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. | ||
53 | * | ||
54 | * @param parameters | ||
55 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
56 | * @pre size of input array must be equal to the number of parameters. | ||
57 | * @return the number of pattern matches found. | ||
58 | */ | ||
59 | public int countMatches(Object[] parameters); | ||
60 | |||
61 | /** | ||
62 | * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. | ||
63 | * | ||
64 | * @param parameterSeedMask | ||
65 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
66 | * bound to a fixed value | ||
67 | * @param parameters | ||
68 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
69 | * parameterSeedMask, so that for each considered match tuple, | ||
70 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
71 | * @return the number of pattern matches found. | ||
72 | * @since 1.7 | ||
73 | */ | ||
74 | public int countMatches(TupleMask parameterSeedMask, ITuple projectedParameterSeed); | ||
75 | |||
76 | /** | ||
77 | * Gives an estimate of the number of different groups the matches are projected into by the given mask | ||
78 | * (e.g. for an identity mask, this means the full match set size). The estimate must meet the required accuracy. | ||
79 | * | ||
80 | * <p> If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. | ||
81 | * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. | ||
82 | * However, caching backends are expected to simply return the indexed (projection) size, initialized on-demand if necessary. | ||
83 | * | ||
84 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
85 | * | ||
86 | * @return if available, an estimate of the cardinality of the projection of the match set, with the desired accuracy. | ||
87 | * | ||
88 | * @since 2.1 | ||
89 | */ | ||
90 | public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy); | ||
91 | |||
92 | /** | ||
93 | * Gives an estimate of the average size of different groups the matches are projected into by the given mask | ||
94 | * (e.g. for an identity mask, this means 1, while for an empty mask, the result is match set size). | ||
95 | * The estimate must meet the required accuracy. | ||
96 | * | ||
97 | * <p> If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. | ||
98 | * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. | ||
99 | * However, caching backends are expected to simply return the exact value from the index, initialized on-demand if necessary. | ||
100 | * | ||
101 | * <p> For an empty match set, zero is acceptable as an exact answer. | ||
102 | * | ||
103 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
104 | * | ||
105 | * @return if available, an estimate of the average size of each projection group of the match set, with the desired accuracy. | ||
106 | * | ||
107 | * @since 2.1 | ||
108 | */ | ||
109 | public default Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
110 | return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, this::estimateCardinality); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. | ||
115 | * Neither determinism nor randomness of selection is guaranteed. | ||
116 | * | ||
117 | * @param parameters | ||
118 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
119 | * @pre size of input array must be equal to the number of parameters. | ||
120 | * @return a match represented in the internal {@link Tuple} representation. | ||
121 | * @since 2.0 | ||
122 | */ | ||
123 | public Optional<Tuple> getOneArbitraryMatch(Object[] parameters); | ||
124 | |||
125 | /** | ||
126 | * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. | ||
127 | * Neither determinism nor randomness of selection is guaranteed. | ||
128 | * | ||
129 | * @param parameterSeedMask | ||
130 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
131 | * bound to a fixed value | ||
132 | * @param parameters | ||
133 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
134 | * parameterSeedMask, so that for each considered match tuple, | ||
135 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
136 | * @return a match represented in the internal {@link Tuple} representation. | ||
137 | * @since 2.0 | ||
138 | */ | ||
139 | public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters); | ||
140 | |||
141 | /** | ||
142 | * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. | ||
143 | * | ||
144 | * @param parameters | ||
145 | * array where each non-null element binds the corresponding pattern parameter to a fixed value. | ||
146 | * @pre size of input array must be equal to the number of parameters. | ||
147 | * @return matches represented in the internal {@link Tuple} representation. | ||
148 | * @since 2.0 | ||
149 | */ | ||
150 | public Stream<Tuple> getAllMatches(Object[] parameters); | ||
151 | |||
152 | /** | ||
153 | * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. | ||
154 | * | ||
155 | * @param parameterSeedMask | ||
156 | * a mask that extracts those parameters of the query (from the entire parameter list) that should be | ||
157 | * bound to a fixed value | ||
158 | * @param parameters | ||
159 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
160 | * parameterSeedMask, so that for each considered match tuple, | ||
161 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold | ||
162 | * @return matches represented in the internal {@link Tuple} representation. | ||
163 | * @since 2.0 | ||
164 | */ | ||
165 | public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters); | ||
166 | |||
167 | /** | ||
168 | * The underlying query evaluator backend. | ||
169 | */ | ||
170 | public IQueryBackend getQueryBackend(); | ||
171 | |||
172 | /** | ||
173 | * Internal method that registers low-level callbacks for match appearance and disappearance. | ||
174 | * | ||
175 | * <p> | ||
176 | * <b>Caution: </b> This is a low-level callback that is invoked when the pattern matcher is not necessarily in a | ||
177 | * consistent state yet. Importantly, no model modification permitted during the callback. | ||
178 | * | ||
179 | * <p> | ||
180 | * The callback can be unregistered via invoking {@link #removeUpdateListener(Object)} with the same tag. | ||
181 | * | ||
182 | * @param listener | ||
183 | * the listener that will be notified of each new match that appears or disappears, starting from now. | ||
184 | * @param listenerTag | ||
185 | * a tag by which to identify the listener for later removal by {@link #removeUpdateListener(Object)}. | ||
186 | * @param fireNow | ||
187 | * if true, the insertion update allback will be immediately invoked on all current matches as a one-time effect. | ||
188 | * | ||
189 | * @throws UnsupportedOperationException if this is a non-incremental backend | ||
190 | * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) | ||
191 | */ | ||
192 | public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow); | ||
193 | |||
194 | /** | ||
195 | * Removes an existing listener previously registered with the given tag. | ||
196 | * | ||
197 | * @throws UnsupportedOperationException if this is a non-incremental backend | ||
198 | * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) | ||
199 | */ | ||
200 | public void removeUpdateListener(final Object listenerTag); | ||
201 | |||
202 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java new file mode 100644 index 00000000..baf7144a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | /** | ||
14 | * Internal interface for the query backend to singal an update to a query result. | ||
15 | * @author Bergmann Gabor | ||
16 | * @since 0.9 | ||
17 | * | ||
18 | */ | ||
19 | public interface IUpdateable { | ||
20 | |||
21 | /** | ||
22 | * This callback method must be free of exceptions, even {@link RuntimeException}s (though not {@link Error}s). | ||
23 | * @param updateElement the tuple that is changed | ||
24 | * @param isInsertion true if the tuple appeared in the result set, false if disappeared from the result set | ||
25 | */ | ||
26 | public void update(Tuple updateElement, boolean isInsertion); | ||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java new file mode 100644 index 00000000..eab92128 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java | |||
@@ -0,0 +1,241 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.backend; | ||
10 | |||
11 | import java.util.AbstractMap; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Objects; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
19 | |||
20 | /** | ||
21 | * Provides VIATRA Query with additional hints on how a query should be evaluated. The same hint can be provided to multiple queries. | ||
22 | * | ||
23 | * <p> This class is immutable. Overriding options will create a new instance. | ||
24 | * | ||
25 | * <p> | ||
26 | * Here be dragons: for advanced users only. | ||
27 | * | ||
28 | * @author Bergmann Gabor | ||
29 | * | ||
30 | */ | ||
31 | public class QueryEvaluationHint { | ||
32 | |||
33 | /** | ||
34 | * @since 2.0 | ||
35 | * | ||
36 | */ | ||
37 | public enum BackendRequirement { | ||
38 | /** | ||
39 | * The current hint does not specify any backend requirement | ||
40 | */ | ||
41 | UNSPECIFIED, | ||
42 | /** | ||
43 | * The current hint specifies that the default search backend of the engine should be used | ||
44 | */ | ||
45 | DEFAULT_SEARCH, | ||
46 | /** | ||
47 | * The current hint specifies that the default caching backend of the engine should be used | ||
48 | */ | ||
49 | DEFAULT_CACHING, | ||
50 | /** | ||
51 | * The current hint specifies that a specific backend is to be used | ||
52 | */ | ||
53 | SPECIFIC | ||
54 | } | ||
55 | |||
56 | final IQueryBackendFactory queryBackendFactory; | ||
57 | final Map<QueryHintOption<?>, Object> backendHintSettings; | ||
58 | final BackendRequirement requirement; | ||
59 | |||
60 | /** | ||
61 | * Specifies the suggested query backend requirements, and value settings for additional backend-specific options. | ||
62 | * | ||
63 | * <p> | ||
64 | * The backend requirement type must not be {@link BackendRequirement#SPECIFIC} - for that case, use the constructor | ||
65 | * {@link #QueryEvaluationHint(Map, IQueryBackendFactory)}. | ||
66 | * | ||
67 | * @param backendHintSettings | ||
68 | * if non-null, each entry in the map overrides backend-specific options regarding query evaluation | ||
69 | * (null-valued map entries permitted to erase hints); passing null means default options associated with | ||
70 | * the query | ||
71 | * @param backendRequirementType | ||
72 | * defines the kind of backend requirement | ||
73 | * @since 2.0 | ||
74 | */ | ||
75 | public QueryEvaluationHint(Map<QueryHintOption<?>, Object> backendHintSettings, BackendRequirement backendRequirementType) { | ||
76 | super(); | ||
77 | Preconditions.checkArgument(backendRequirementType != null, "Specific requirement needs to be set"); | ||
78 | Preconditions.checkArgument(backendRequirementType != BackendRequirement.SPECIFIC, "Specific backend requirement needs providing a corresponding backend type"); | ||
79 | this.queryBackendFactory = null; | ||
80 | this.requirement = backendRequirementType; | ||
81 | this.backendHintSettings = (backendHintSettings == null) | ||
82 | ? Collections.<QueryHintOption<?>, Object> emptyMap() | ||
83 | : new HashMap<>(backendHintSettings); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Specifies the suggested query backend, and value settings for additional backend-specific options. The first | ||
88 | * parameter can be null; if the second parameter is null, it is expected that the other constructor is called | ||
89 | * instead with a {@link BackendRequirement#UNSPECIFIED} parameter. | ||
90 | * | ||
91 | * @param backendHintSettings | ||
92 | * if non-null, each entry in the map overrides backend-specific options regarding query evaluation | ||
93 | * (null-valued map entries permitted to erase hints); passing null means default options associated with | ||
94 | * the query | ||
95 | * @param queryBackendFactory | ||
96 | * overrides the query evaluator algorithm; passing null retains the default algorithm associated with | ||
97 | * the query | ||
98 | * @since 1.5 | ||
99 | */ | ||
100 | public QueryEvaluationHint( | ||
101 | Map<QueryHintOption<?>, Object> backendHintSettings, | ||
102 | IQueryBackendFactory queryBackendFactory) { | ||
103 | super(); | ||
104 | this.queryBackendFactory = queryBackendFactory; | ||
105 | this.requirement = (queryBackendFactory == null) ? BackendRequirement.UNSPECIFIED : BackendRequirement.SPECIFIC; | ||
106 | this.backendHintSettings = (backendHintSettings == null) | ||
107 | ? Collections.<QueryHintOption<?>, Object> emptyMap() | ||
108 | : new HashMap<>(backendHintSettings); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Returns the backend requirement described by this hint. If a specific backend is required, that can be queried by {@link #getQueryBackendFactory()}. | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | public BackendRequirement getQueryBackendRequirementType() { | ||
116 | return requirement; | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * A suggestion for choosing the query evaluator algorithm. | ||
121 | * | ||
122 | * <p> | ||
123 | * Returns null iff {@link #getQueryBackendRequirementType()} does not return {@link BackendRequirement#SPECIFIC}; | ||
124 | * in such cases a corresponding default backend is selected inside the engine | ||
125 | */ | ||
126 | public IQueryBackendFactory getQueryBackendFactory() { | ||
127 | return queryBackendFactory; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Each entry in the immutable map overrides backend-specific options regarding query evaluation. | ||
132 | * | ||
133 | * <p>The map is non-null, even if empty. | ||
134 | * Null-valued map entries are also permitted to erase hints via {@link #overrideBy(QueryEvaluationHint)}. | ||
135 | * | ||
136 | * @since 1.5 | ||
137 | */ | ||
138 | public Map<QueryHintOption<?>, Object> getBackendHintSettings() { | ||
139 | return backendHintSettings; | ||
140 | } | ||
141 | |||
142 | |||
143 | /** | ||
144 | * Override values in this hint and return a consolidated instance. | ||
145 | * | ||
146 | * @since 1.4 | ||
147 | */ | ||
148 | public QueryEvaluationHint overrideBy(QueryEvaluationHint overridingHint){ | ||
149 | if (overridingHint == null) | ||
150 | return this; | ||
151 | |||
152 | BackendRequirement overriddenRequirement = this.getQueryBackendRequirementType(); | ||
153 | if (overridingHint.getQueryBackendRequirementType() != BackendRequirement.UNSPECIFIED) { | ||
154 | overriddenRequirement = overridingHint.getQueryBackendRequirementType(); | ||
155 | } | ||
156 | Map<QueryHintOption<?>, Object> hints = new HashMap<>(this.getBackendHintSettings()); | ||
157 | if (overridingHint.getBackendHintSettings() != null) { | ||
158 | hints.putAll(overridingHint.getBackendHintSettings()); | ||
159 | } | ||
160 | if (overriddenRequirement == BackendRequirement.SPECIFIC) { | ||
161 | IQueryBackendFactory factory = this.getQueryBackendFactory(); | ||
162 | if (overridingHint.getQueryBackendFactory() != null) { | ||
163 | factory = overridingHint.getQueryBackendFactory(); | ||
164 | } | ||
165 | return new QueryEvaluationHint(hints, factory); | ||
166 | } else { | ||
167 | return new QueryEvaluationHint(hints, overriddenRequirement); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * Returns whether the given hint option is overridden. | ||
173 | * @since 1.5 | ||
174 | */ | ||
175 | public boolean isOptionOverridden(QueryHintOption<?> option) { | ||
176 | return getBackendHintSettings().containsKey(option); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Returns the value of the given hint option from the given hint collection, or null if not defined. | ||
181 | * @since 1.5 | ||
182 | */ | ||
183 | @SuppressWarnings("unchecked") | ||
184 | public <HintValue> HintValue getValueOrNull(QueryHintOption<HintValue> option) { | ||
185 | return (HintValue) getBackendHintSettings().get(option); | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Returns the value of the given hint option from the given hint collection, or the default value if not defined. | ||
190 | * Intended to be called by backends to find out the definitive value that should be considered. | ||
191 | * @since 1.5 | ||
192 | */ | ||
193 | public <HintValue> HintValue getValueOrDefault(QueryHintOption<HintValue> option) { | ||
194 | return option.getValueOrDefault(this); | ||
195 | } | ||
196 | |||
197 | @Override | ||
198 | public int hashCode() { | ||
199 | return Objects.hash(backendHintSettings, queryBackendFactory, requirement); | ||
200 | } | ||
201 | |||
202 | @Override | ||
203 | public boolean equals(Object obj) { | ||
204 | if (this == obj) | ||
205 | return true; | ||
206 | if (obj == null) | ||
207 | return false; | ||
208 | if (getClass() != obj.getClass()) | ||
209 | return false; | ||
210 | QueryEvaluationHint other = (QueryEvaluationHint) obj; | ||
211 | return Objects.equals(backendHintSettings, other.backendHintSettings) | ||
212 | && | ||
213 | Objects.equals(queryBackendFactory, other.queryBackendFactory) | ||
214 | && | ||
215 | Objects.equals(requirement, other.requirement) | ||
216 | ; | ||
217 | } | ||
218 | |||
219 | @Override | ||
220 | public String toString() { | ||
221 | StringBuilder sb = new StringBuilder(); | ||
222 | |||
223 | if (getQueryBackendFactory() != null) | ||
224 | sb.append("backend: ").append(getQueryBackendFactory().getBackendClass().getSimpleName()); | ||
225 | if (! backendHintSettings.isEmpty()) { | ||
226 | sb.append("hints: "); | ||
227 | if(backendHintSettings instanceof AbstractMap){ | ||
228 | sb.append(backendHintSettings.toString()); | ||
229 | } else { | ||
230 | // we have to iterate on the contents | ||
231 | |||
232 | String joinedHintMap = backendHintSettings.entrySet().stream() | ||
233 | .map(setting -> setting.getKey() + "=" + setting.getValue()).collect(Collectors.joining(", ")); | ||
234 | sb.append('{').append(joinedHintMap).append('}'); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | final String result = sb.toString(); | ||
239 | return result.isEmpty() ? "defaults" : result; | ||
240 | } | ||
241 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java new file mode 100644 index 00000000..2c6bb4de --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java | |||
@@ -0,0 +1,122 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.backend; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.Objects; | ||
13 | |||
14 | /** | ||
15 | * Each instance of this class corresponds to a given hint option. | ||
16 | * | ||
17 | * It is recommended to expose options to clients (and query backends) as public static fields. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * @since 1.5 | ||
21 | */ | ||
22 | public class QueryHintOption<HintValue> { | ||
23 | |||
24 | private String optionQualifiedName; | ||
25 | private HintValue defaultValue; | ||
26 | |||
27 | /** | ||
28 | * Instantiates an option object with the given name and default value. | ||
29 | */ | ||
30 | public QueryHintOption(String optionQualifiedName, HintValue defaultValue) { | ||
31 | this.optionQualifiedName = optionQualifiedName; | ||
32 | this.defaultValue = defaultValue; | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * This is the recommended constructor for hint options defined as static fields within an enclosing class. | ||
37 | * Combines the qualified name of the hint from the (qualified) name of the enclosing class and a local name (unique within that class). | ||
38 | */ | ||
39 | public <T extends HintValue> QueryHintOption(Class<?> optionNamespace, String optionLocalName, T defaultValue) { | ||
40 | this(String.format("%s@%s", optionLocalName, optionNamespace.getName()), defaultValue); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Returns the qualified name, a unique string identifier of the option. | ||
45 | */ | ||
46 | public String getQualifiedName() { | ||
47 | return optionQualifiedName; | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * Returns the default value of this hint option, which is to be used by query backends in the case no overriding value is assigned. | ||
52 | */ | ||
53 | public HintValue getDefaultValue() { | ||
54 | return defaultValue; | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Returns the value of this hint option from the given hint collection, or the default value if not defined. | ||
59 | * Intended to be called by backends to find out the definitive value that should be considered. | ||
60 | */ | ||
61 | @SuppressWarnings("unchecked") | ||
62 | public HintValue getValueOrDefault(QueryEvaluationHint hints) { | ||
63 | Object value = hints.getValueOrNull(this); | ||
64 | if (value == null) | ||
65 | return getDefaultValue(); | ||
66 | else { | ||
67 | return (HintValue) value; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | |||
72 | /** | ||
73 | * Returns the value of this hint option from the given hint collection, or null if not defined. | ||
74 | */ | ||
75 | public HintValue getValueOrNull(QueryEvaluationHint hints) { | ||
76 | return hints.getValueOrNull(this); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Returns whether this hint option is defined in the given hint collection. | ||
81 | */ | ||
82 | public boolean isOverriddenIn(QueryEvaluationHint hints) { | ||
83 | return hints.isOptionOverridden(this); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Puts a value of this hint option into an option-to-value map. | ||
88 | * | ||
89 | * <p> This method is offered in lieu of a builder API. | ||
90 | * Use this method on any number of hint options in order to populate an option-value map. | ||
91 | * Then instantiate the immutable {@link QueryEvaluationHint} using the map. | ||
92 | * | ||
93 | * @see #insertValueIfNondefault(Map, Object) | ||
94 | * @return the hint value that was previously present in the map under this hint option, carrying over the semantics of {@link Map#put(Object, Object)}. | ||
95 | */ | ||
96 | @SuppressWarnings("unchecked") | ||
97 | public HintValue insertOverridingValue(Map<QueryHintOption<?>, Object> hints, HintValue overridingValue) { | ||
98 | return (HintValue) hints.put(this, overridingValue); | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Puts a value of this hint option into an option-to-value map, if the given value differs from the default value of the option. | ||
103 | * If the default value is provided instead, then the map is not updated. | ||
104 | * | ||
105 | * <p> This method is offered in lieu of a builder API. | ||
106 | * Use this method on any number of hint options in order to populate an option-value map. | ||
107 | * Then instantiate the immutable {@link QueryEvaluationHint} using the map. | ||
108 | * | ||
109 | * @see #insertOverridingValue(Map, Object) | ||
110 | * @since 2.0 | ||
111 | */ | ||
112 | public void insertValueIfNondefault(Map<QueryHintOption<?>, Object> hints, HintValue overridingValue) { | ||
113 | if (!Objects.equals(defaultValue, overridingValue)) | ||
114 | hints.put(this, overridingValue); | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public String toString() { | ||
119 | return optionQualifiedName; | ||
120 | } | ||
121 | |||
122 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java new file mode 100644 index 00000000..6ec6d53e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java | |||
@@ -0,0 +1,74 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.backend; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | |||
15 | /** | ||
16 | * Uniform way of requesting result providers for pattern calls within queries. | ||
17 | * Intended users are query backends, for calling other backends to deliver results of dependee queries. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * @since 2.1 | ||
21 | */ | ||
22 | public class ResultProviderRequestor { | ||
23 | IQueryBackend callerBackend; | ||
24 | IQueryResultProviderAccess resultProviderAccess; | ||
25 | IQueryBackendHintProvider hintProvider; | ||
26 | ICallDelegationStrategy delegationStrategy; | ||
27 | QueryEvaluationHint callerHint; | ||
28 | QueryEvaluationHint universalOverride; | ||
29 | |||
30 | |||
31 | /** | ||
32 | * @param callerBackend the actual backend evaluating the calling pattern. | ||
33 | * @param resultProviderAccess | ||
34 | * @param hintProvider | ||
35 | * @param delegationStrategy | ||
36 | * @param callerHint a hint under which the calling pattern is evaluated, | ||
37 | * @param universalOverride if non-null, overrides the hint with extra options <i>after</i> the {@link ICallDelegationStrategy} | ||
38 | */ | ||
39 | public ResultProviderRequestor(IQueryBackend callerBackend, IQueryResultProviderAccess resultProviderAccess, | ||
40 | IQueryBackendHintProvider hintProvider, ICallDelegationStrategy delegationStrategy, | ||
41 | QueryEvaluationHint callerHint, QueryEvaluationHint universalOverride) { | ||
42 | super(); | ||
43 | this.callerBackend = callerBackend; | ||
44 | this.resultProviderAccess = resultProviderAccess; | ||
45 | this.hintProvider = hintProvider; | ||
46 | this.delegationStrategy = delegationStrategy; | ||
47 | this.callerHint = callerHint; | ||
48 | this.universalOverride = universalOverride; | ||
49 | } | ||
50 | |||
51 | |||
52 | |||
53 | |||
54 | /** | ||
55 | * | ||
56 | * @param call a {@link PConstraint} in a query that calls another query. | ||
57 | * @param spotOverride if non-null, overrides the hint with extra options <i>after</i> the {@link ICallDelegationStrategy} | ||
58 | * and the universal override specified in the constructor | ||
59 | * @return the obtained result provider | ||
60 | */ | ||
61 | public IQueryResultProvider requestResultProvider(IQueryReference call, QueryEvaluationHint spotOverride) { | ||
62 | QueryEvaluationHint hints = | ||
63 | delegationStrategy.transformHints(call, callerHint, callerBackend, hintProvider); | ||
64 | |||
65 | if (universalOverride != null) | ||
66 | hints = hints.overrideBy(universalOverride); | ||
67 | |||
68 | if (spotOverride != null) | ||
69 | hints = hints.overrideBy(spotOverride); | ||
70 | |||
71 | return resultProviderAccess.getResultProvider(call.getReferredQuery(), hints); | ||
72 | } | ||
73 | |||
74 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java new file mode 100644 index 00000000..99611758 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java | |||
@@ -0,0 +1,69 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. | ||
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.context; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Comparator; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | /** | ||
19 | * Common abstract class for implementers of {@link IQueryMetaContext} | ||
20 | * | ||
21 | * @author Grill Balázs | ||
22 | * @since 1.3 | ||
23 | * | ||
24 | */ | ||
25 | public abstract class AbstractQueryMetaContext implements IQueryMetaContext { | ||
26 | |||
27 | /** | ||
28 | * @since 2.0 | ||
29 | */ | ||
30 | @Override | ||
31 | public Map<InputKeyImplication, Set<InputKeyImplication>> getConditionalImplications(IInputKey implyingKey) { | ||
32 | return new HashMap<>(); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * @since 1.6 | ||
37 | */ | ||
38 | @Override | ||
39 | public boolean canLeadOutOfScope(IInputKey key) { | ||
40 | return key.getArity() > 1; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * @since 1.6 | ||
45 | */ | ||
46 | @Override | ||
47 | public Comparator<IInputKey> getSuggestedEliminationOrdering() { | ||
48 | return (o1, o2) -> 0; | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * @since 1.6 | ||
53 | */ | ||
54 | @Override | ||
55 | public Collection<InputKeyImplication> getWeakenedAlternatives(IInputKey implyingKey) { | ||
56 | return Collections.emptySet(); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public boolean isPosetKey(IInputKey key) { | ||
61 | return false; | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public IPosetComparator getPosetComparator(Iterable<IInputKey> key) { | ||
66 | return null; | ||
67 | } | ||
68 | |||
69 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java new file mode 100644 index 00000000..c797eff9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java | |||
@@ -0,0 +1,21 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. | ||
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.context; | ||
10 | |||
11 | /** | ||
12 | * This class is intended to be extended by implementors. The main purpose of this abstract implementation to protect | ||
13 | * implementors from future changes in the interface. | ||
14 | * | ||
15 | * @author Grill Balázs | ||
16 | * @since 1.4 | ||
17 | * | ||
18 | */ | ||
19 | public abstract class AbstractQueryRuntimeContext implements IQueryRuntimeContext { | ||
20 | |||
21 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java new file mode 100644 index 00000000..4dbcca88 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java | |||
@@ -0,0 +1,45 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context; | ||
10 | |||
11 | /** | ||
12 | * An input key identifies an input (extensional) relation, such as the instance set of a given node or edge type, or the direct containment relation. | ||
13 | * | ||
14 | * <p> The input key, at the very minimum, is associated with an arity (number of columns), a user-friendly name, and a string identifier (for distributive purposes). | ||
15 | * | ||
16 | * <p> The input key itself must be an immutable data object that properly overrides equals() and hashCode(). | ||
17 | * It must be instantiable without using the query context object, so that query specifications may construct the appropriate PQueries. | ||
18 | * | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public interface IInputKey { | ||
23 | |||
24 | /** | ||
25 | * A user-friendly name that can be shown on screen for debug purposes, included in exceptions, etc. | ||
26 | */ | ||
27 | public String getPrettyPrintableName(); | ||
28 | /** | ||
29 | * An internal string identifier that can be used to uniquely identify to input key (relevant for distributed applications). | ||
30 | */ | ||
31 | public String getStringID(); | ||
32 | |||
33 | /** | ||
34 | * The width of tuples in this relation. | ||
35 | */ | ||
36 | public int getArity(); | ||
37 | |||
38 | /** | ||
39 | * Returns true iff instance tuples of the key can be enumerated. | ||
40 | * <p> If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. | ||
41 | */ | ||
42 | boolean isEnumerable(); | ||
43 | |||
44 | |||
45 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java new file mode 100644 index 00000000..e2d5bcee --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, 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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | /** | ||
14 | * Implementations of this interface aid the query engine with the ordering of poset elements. This information is | ||
15 | * particularly important in the delete and re-derive evaluation mode because they let the engine identify monotone | ||
16 | * change pairs. | ||
17 | * | ||
18 | * @author Tamas Szabo | ||
19 | * @since 1.6 | ||
20 | */ | ||
21 | public interface IPosetComparator { | ||
22 | |||
23 | /** | ||
24 | * Returns true if the 'left' tuple of poset elements is smaller or equal than the 'right' tuple of poset elements according to | ||
25 | * the partial order that this poset comparator employs. | ||
26 | * | ||
27 | * @param left | ||
28 | * the left tuple of poset elements | ||
29 | * @param right | ||
30 | * the right tuple of poset elements | ||
31 | * @return true if left is smaller or equal to right, false otherwise | ||
32 | */ | ||
33 | public boolean isLessOrEqual(final Tuple left, final Tuple right); | ||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java new file mode 100644 index 00000000..04f00aaa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs Ltd. | ||
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.context; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
14 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * This interface is a collector which holds every API that is provided by the engine to control | ||
20 | * the operation of the backends. | ||
21 | * | ||
22 | * @since 1.5 | ||
23 | * @noimplement This interface is not intended to be implemented by clients. | ||
24 | */ | ||
25 | public interface IQueryBackendContext { | ||
26 | |||
27 | Logger getLogger(); | ||
28 | |||
29 | IQueryRuntimeContext getRuntimeContext(); | ||
30 | |||
31 | IQueryCacheContext getQueryCacheContext(); | ||
32 | |||
33 | IQueryBackendHintProvider getHintProvider(); | ||
34 | |||
35 | IQueryResultProviderAccess getResultProviderAccess(); | ||
36 | |||
37 | QueryAnalyzer getQueryAnalyzer(); | ||
38 | |||
39 | /** | ||
40 | * @since 2.0 | ||
41 | */ | ||
42 | IMatcherCapability getRequiredMatcherCapability(PQuery query, QueryEvaluationHint overrideHints); | ||
43 | |||
44 | /** | ||
45 | * @since 1.6 | ||
46 | */ | ||
47 | boolean areUpdatesDelayed(); | ||
48 | |||
49 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java new file mode 100644 index 00000000..617207f6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * Provides information on already cached queries to query evaluator backends at runtime. | ||
18 | * | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public interface IQueryCacheContext { | ||
23 | |||
24 | /** | ||
25 | * Checks if there already is a caching result provider for the given query. | ||
26 | * <p> Returns false if called while the caching result provider of the given query is being constructed in the first place. | ||
27 | */ | ||
28 | public boolean isResultCached(PQuery query); | ||
29 | |||
30 | /** | ||
31 | * Returns a caching result provider for the given query; it must be constructed if it does not exist yet. | ||
32 | * <p> <b>Caution:</b> behavior undefined if called while the caching result provider of the given query is being constructed. | ||
33 | * Beware of infinite loops. | ||
34 | * <p> <b>Postcondition:</b> {@link IQueryBackend#isCaching()} returns true for the {@link #getQueryBackend()} of the returned provider | ||
35 | * | ||
36 | * @throws ViatraQueryRuntimeException | ||
37 | */ | ||
38 | public IQueryResultProvider getCachingResultProvider(PQuery query); | ||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java new file mode 100644 index 00000000..4c22a3cb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java | |||
@@ -0,0 +1,98 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Comparator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | /** | ||
17 | * Provides metamodel information (relationship of input keys) to query evaluator backends at runtime and at query planning time. | ||
18 | * | ||
19 | * @noimplement Implementors should extend {@link AbstractQueryMetaContext} instead of directly implementing this interface. | ||
20 | * @author Bergmann Gabor | ||
21 | */ | ||
22 | public interface IQueryMetaContext { | ||
23 | |||
24 | /** | ||
25 | * Returns true iff instance tuples of the given key can be enumerated. | ||
26 | * <p> If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. | ||
27 | * <p> Equivalent to {@link IInputKey#isEnumerable()}. | ||
28 | */ | ||
29 | boolean isEnumerable(IInputKey key); | ||
30 | |||
31 | /** | ||
32 | * Returns true iff the set of instance tuples of the given key is immutable. | ||
33 | * <p> If false, the runtime provides notifications upon change. | ||
34 | */ | ||
35 | boolean isStateless(IInputKey key); | ||
36 | |||
37 | /** | ||
38 | * Returns a set of implications (weakened alternatives), | ||
39 | * with a suggestion for the query planner that satisfying them first may help in satisfying the implying key. | ||
40 | * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. | ||
41 | * <p> Must follow directly or transitively from implications of {@link #getImplications(IInputKey)}. | ||
42 | * @since 1.6 | ||
43 | */ | ||
44 | Collection<InputKeyImplication> getWeakenedAlternatives(IInputKey implyingKey); | ||
45 | |||
46 | /** | ||
47 | * Returns known direct implications, e.g. edge supertypes, edge opposites, node type constraints, etc. | ||
48 | * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. | ||
49 | */ | ||
50 | Collection<InputKeyImplication> getImplications(IInputKey implyingKey); | ||
51 | |||
52 | /** | ||
53 | * Returns known "double dispatch" implications, where the given implying key implies other input keys under certain additional conditions (themselves input keys). | ||
54 | * For example, a "type x, unscoped" input key may imply the "type x, in scope" input key under the condition of the input key "x is in scope" | ||
55 | * | ||
56 | * <p> Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys (either as the implying key or as the additional condition). | ||
57 | * <p> Note that symmetry is not required, i.e. the additional conditions do not have to list the same conditional implication. | ||
58 | * @return multi-map, where the keys are additional conditions and the values are input key implications jointly implied by the condition and the given implying key. | ||
59 | * @since 2.0 | ||
60 | */ | ||
61 | Map<InputKeyImplication, Set<InputKeyImplication>> getConditionalImplications(IInputKey implyingKey); | ||
62 | |||
63 | /** | ||
64 | * Returns functional dependencies of the input key expressed in terms of column indices. | ||
65 | * | ||
66 | * <p> Each entry of the map is a functional dependency rule, where the entry key specifies source columns and the entry value specifies target columns. | ||
67 | */ | ||
68 | Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key); | ||
69 | |||
70 | /** | ||
71 | * For query normalizing, this is the order suggested for trying to eliminate input keys. | ||
72 | * @since 1.6 | ||
73 | */ | ||
74 | Comparator<IInputKey> getSuggestedEliminationOrdering(); | ||
75 | |||
76 | /** | ||
77 | * Tells whether the given {@link IInputKey} is an edge and may lead out of scope. | ||
78 | * | ||
79 | * @since 1.6 | ||
80 | */ | ||
81 | boolean canLeadOutOfScope(IInputKey key); | ||
82 | |||
83 | /** | ||
84 | * Returns true if the given {@link IInputKey} represents a poset type. | ||
85 | * @since 1.6 | ||
86 | */ | ||
87 | boolean isPosetKey(IInputKey key); | ||
88 | |||
89 | /** | ||
90 | * Returns an {@link IPosetComparator} for the given set of {@link IInputKey}s. | ||
91 | * | ||
92 | * @param keys an iterable collection of input keys | ||
93 | * @return the poset comparator | ||
94 | * @since 1.6 | ||
95 | */ | ||
96 | IPosetComparator getPosetComparator(Iterable<IInputKey> keys); | ||
97 | |||
98 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java new file mode 100644 index 00000000..7fecd01a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs Ltd. | ||
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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | |||
15 | /** | ||
16 | * This interface exposes API to request {@link IQueryResultProvider} for {@link PQuery} instances. | ||
17 | * | ||
18 | * @author Grill Balázs | ||
19 | * @since 1.5 | ||
20 | * @noimplement This interface is not intended to be implemented by clients. | ||
21 | */ | ||
22 | public interface IQueryResultProviderAccess { | ||
23 | |||
24 | /** | ||
25 | * Get a result provider for the given {@link PQuery}, which conforms the capabilities requested by the | ||
26 | * given {@link QueryEvaluationHint} object. | ||
27 | * @throws ViatraQueryRuntimeException | ||
28 | */ | ||
29 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint overrideHints); | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java new file mode 100644 index 00000000..c2e90614 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java | |||
@@ -0,0 +1,281 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.Optional; | ||
13 | import java.util.concurrent.Callable; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
20 | |||
21 | /** | ||
22 | * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. | ||
23 | * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. | ||
24 | * | ||
25 | * @author Bergmann Gabor | ||
26 | * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. | ||
27 | */ | ||
28 | public interface IQueryRuntimeContext { | ||
29 | /** | ||
30 | * Provides metamodel-specific info independent of the runtime instance model. | ||
31 | */ | ||
32 | public IQueryMetaContext getMetaContext(); | ||
33 | |||
34 | |||
35 | /** | ||
36 | * The given callable will be executed, and all model traversals will be delayed until the execution is done. If | ||
37 | * there are any outstanding information to be read from the model, a single coalesced model traversal will | ||
38 | * initialize the caches and deliver the notifications. | ||
39 | * | ||
40 | * <p> Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. | ||
41 | * | ||
42 | * <p> <b>Caution: </b> results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. | ||
43 | * For example, if a certain input key is not cached yet, an empty relation may be reported during <code>callable.call()</code>; the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. | ||
44 | * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). | ||
45 | * | ||
46 | * @param callable | ||
47 | */ | ||
48 | public abstract <V> V coalesceTraversals(Callable<V> callable) throws InvocationTargetException; | ||
49 | /** | ||
50 | * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). | ||
51 | */ | ||
52 | public boolean isCoalescing(); | ||
53 | |||
54 | /** | ||
55 | * Returns true if index is available for the given key providing the given service. | ||
56 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
57 | * @since 1.4 | ||
58 | */ | ||
59 | public boolean isIndexed(IInputKey key, IndexingService service); | ||
60 | |||
61 | /** | ||
62 | * If the given (enumerable) input key is not yet indexed, the model will be traversed | ||
63 | * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) | ||
64 | * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging | ||
65 | * multiple indexing requests to an appropriate level. | ||
66 | * | ||
67 | * <p><b>Postcondition:</b> After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key | ||
68 | * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. | ||
69 | * | ||
70 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
71 | * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
72 | * @since 1.4 | ||
73 | */ | ||
74 | public void ensureIndexed(IInputKey key, IndexingService service); | ||
75 | |||
76 | /** | ||
77 | * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. | ||
78 | * | ||
79 | * @param key an input key | ||
80 | * @param seedMask | ||
81 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
82 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask. | ||
83 | * @param seed | ||
84 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
85 | * parameterSeedMask, so that for each considered match tuple, | ||
86 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
87 | * | ||
88 | * @return the number of tuples in the model for the given key and seed | ||
89 | * | ||
90 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
91 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
92 | * @since 1.7 | ||
93 | */ | ||
94 | public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); | ||
95 | |||
96 | |||
97 | /** | ||
98 | * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask | ||
99 | * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. | ||
100 | * | ||
101 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. | ||
102 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. | ||
103 | * | ||
104 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
105 | * | ||
106 | * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. | ||
107 | * | ||
108 | * @since 2.1 | ||
109 | */ | ||
110 | public Optional<Long> estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); | ||
111 | |||
112 | |||
113 | /** | ||
114 | * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask | ||
115 | * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). | ||
116 | * The estimate must meet the required accuracy. | ||
117 | * | ||
118 | * <p> Must accept any input key, even non-enumerables or those not recognized by this runtime context. | ||
119 | * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. | ||
120 | * | ||
121 | * <p> For an empty relation, zero is acceptable as an exact answer. | ||
122 | * | ||
123 | * <p> PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. | ||
124 | * | ||
125 | * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. | ||
126 | * | ||
127 | * @since 2.1 | ||
128 | */ | ||
129 | public default Optional<Double> estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { | ||
130 | if (key.isEnumerable()) { | ||
131 | return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, | ||
132 | (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); | ||
133 | } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); | ||
134 | } | ||
135 | |||
136 | |||
137 | /** | ||
138 | * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. | ||
139 | * | ||
140 | * @param key an input key | ||
141 | * @param seedMask | ||
142 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
143 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most once in seedMask. | ||
144 | * @param seed | ||
145 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
146 | * parameterSeedMask, so that for each considered match tuple, | ||
147 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
148 | * @return the tuples in the model for the given key and seed | ||
149 | * | ||
150 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
151 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
152 | * @since 1.7 | ||
153 | */ | ||
154 | public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); | ||
155 | |||
156 | /** | ||
157 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples | ||
158 | * are bound by the seed except for one. | ||
159 | * | ||
160 | * <p> | ||
161 | * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given | ||
162 | * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. | ||
163 | * | ||
164 | * @param key | ||
165 | * an input key | ||
166 | * @param seedMask | ||
167 | * a mask that extracts those parameters of the input key (from the entire parameter list) that should be | ||
168 | * bound to a fixed value; must not be null. <strong>Note</strong>: any given index must occur at most | ||
169 | * once in seedMask, and seedMask must include all parameters in any arbitrary order except one. | ||
170 | * @param seed | ||
171 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
172 | * parameterSeedMask, so that for each considered match tuple, | ||
173 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
174 | * @return the objects in the model for the given key and seed | ||
175 | * | ||
176 | * <p> | ||
177 | * <b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
178 | * @throws IllegalArgumentException | ||
179 | * if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
180 | * @since 1.7 | ||
181 | */ | ||
182 | public Iterable<? extends Object> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); | ||
183 | |||
184 | /** | ||
185 | * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples | ||
186 | * are bound by the seed. | ||
187 | * | ||
188 | * <p> | ||
189 | * Returns whether the given tuple is in the extensional relation identified by the input key. | ||
190 | * | ||
191 | * <p> | ||
192 | * Note: this call works for non-enumerable input keys as well. | ||
193 | * | ||
194 | * @param key | ||
195 | * an input key | ||
196 | * @param seed | ||
197 | * the tuple of fixed values restricting the match set to be considered, in the same order as given in | ||
198 | * parameterSeedMask, so that for each considered match tuple, | ||
199 | * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. | ||
200 | * @return true iff there is at least a single tuple contained in the relation that corresponds to the seed tuple | ||
201 | * @since 2.0 | ||
202 | */ | ||
203 | public boolean containsTuple(IInputKey key, ITuple seed); | ||
204 | |||
205 | |||
206 | /** | ||
207 | * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. | ||
208 | * <p> This should be called after invoking | ||
209 | * | ||
210 | * @param key an input key | ||
211 | * @param seed can be null or a tuple with matching arity; | ||
212 | * if non-null, only those updates in the model are notified about | ||
213 | * that match the seed at positions where the seed is non-null. | ||
214 | * @param listener will be notified of future changes | ||
215 | * | ||
216 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
217 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
218 | */ | ||
219 | public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); | ||
220 | |||
221 | /** | ||
222 | * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. | ||
223 | * | ||
224 | * @param key an input key | ||
225 | * @param seed can be null or a tuple with matching arity; | ||
226 | * if non-null, only those updates in the model are notified about | ||
227 | * that match the seed at positions where the seed is non-null. | ||
228 | * @param listener will no longer be notified of future changes | ||
229 | * | ||
230 | * <p><b>Precondition:</b> the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
231 | * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. | ||
232 | */ | ||
233 | public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); | ||
234 | /* | ||
235 | TODO: uniqueness | ||
236 | */ | ||
237 | |||
238 | /** | ||
239 | * Wraps the external element into the internal representation that is to be used by the query backend | ||
240 | * <p> model element -> internal object. | ||
241 | * <p> null must be mapped to null. | ||
242 | */ | ||
243 | public Object wrapElement(Object externalElement); | ||
244 | |||
245 | /** | ||
246 | * Unwraps the internal representation of the element into its original form | ||
247 | * <p> internal object -> model element | ||
248 | * <p> null must be mapped to null. | ||
249 | */ | ||
250 | public Object unwrapElement(Object internalElement); | ||
251 | |||
252 | /** | ||
253 | * Unwraps the tuple of elements into the internal representation that is to be used by the query backend | ||
254 | * <p> model elements -> internal objects | ||
255 | * <p> null must be mapped to null. | ||
256 | */ | ||
257 | public Tuple wrapTuple(Tuple externalElements); | ||
258 | |||
259 | /** | ||
260 | * Unwraps the tuple of internal representations of elements into their original forms | ||
261 | * <p> internal objects -> model elements | ||
262 | * <p> null must be mapped to null. | ||
263 | */ | ||
264 | public Tuple unwrapTuple(Tuple internalElements); | ||
265 | |||
266 | /** | ||
267 | * Starts wildcard indexing for the given service. After this call, no registration is required for this {@link IndexingService}. | ||
268 | * a previously set wildcard level cannot be lowered, only extended. | ||
269 | * @since 1.4 | ||
270 | */ | ||
271 | public void ensureWildcardIndexing(IndexingService service); | ||
272 | |||
273 | /** | ||
274 | * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as | ||
275 | * the indexing is finished. The callback is executed only once, then is removed from the callback queue. | ||
276 | * @param traversalCallback | ||
277 | * @throws InvocationTargetException | ||
278 | * @since 1.4 | ||
279 | */ | ||
280 | public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; | ||
281 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java new file mode 100644 index 00000000..7be27d56 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | /** | ||
14 | * Listens for changes in the runtime context. | ||
15 | * @author Bergmann Gabor | ||
16 | * | ||
17 | */ | ||
18 | public interface IQueryRuntimeContextListener { | ||
19 | |||
20 | /** | ||
21 | * The given tuple was inserted into or removed from the input relation indicated by the given key. | ||
22 | * @param key the key identifying the input relation that was updated | ||
23 | * @param updateTuple the tuple that was inserted or removed | ||
24 | * @param isInsertion true if it was an insertion, false otherwise. | ||
25 | */ | ||
26 | public void update(IInputKey key, Tuple updateTuple, boolean isInsertion); | ||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java new file mode 100644 index 00000000..8210765d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.context; | ||
10 | |||
11 | /** | ||
12 | * These are the different services which can be provided by an {@link IQueryRuntimeContext} implementation. | ||
13 | * | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public enum IndexingService { | ||
19 | |||
20 | /** | ||
21 | * Cardinality information is available. Makes possible to calculate | ||
22 | * unseeded calls of {@link IQueryRuntimeContext#countTuples(IInputKey, tools.refinery.viatra.runtime.matchers.tuple.Tuple)} | ||
23 | */ | ||
24 | STATISTICS, | ||
25 | |||
26 | /** | ||
27 | * The indexer can provide notifications about changes in the model. | ||
28 | */ | ||
29 | NOTIFICATIONS, | ||
30 | |||
31 | /** | ||
32 | * Enables enumeration of instances and reverse-navigation. | ||
33 | */ | ||
34 | INSTANCES | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java new file mode 100644 index 00000000..2a403810 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java | |||
@@ -0,0 +1,103 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | |||
15 | /** | ||
16 | * Data object representing the implication of an input key, in use cases including edge supertypes, edge opposites, node type constraints, etc. | ||
17 | * | ||
18 | * <p> Each instance tuple of the <i>implying input key</i> (if given) implies the presence of an instance tuple of the <i>implied input key</i> consisting of elements of the original tuple at given positions. | ||
19 | * When the input key is null, it is not an input constraint but some other source that implies input keys. | ||
20 | * | ||
21 | * <p> The implication is an immutable data object. | ||
22 | * | ||
23 | * @author Bergmann Gabor | ||
24 | * | ||
25 | */ | ||
26 | public final class InputKeyImplication { | ||
27 | private IInputKey implyingKey; | ||
28 | private IInputKey impliedKey; | ||
29 | private List<Integer> impliedIndices; | ||
30 | |||
31 | /** | ||
32 | * Optional. Instance tuples of this input key imply an instance tuple of another key. | ||
33 | * Sometimes it is not an input key that implies other input keys, so this attribute can be null. | ||
34 | */ | ||
35 | public IInputKey getImplyingKey() { | ||
36 | return implyingKey; | ||
37 | } | ||
38 | /** | ||
39 | * An instance tuple of this input key is implied by another key. | ||
40 | */ | ||
41 | public IInputKey getImpliedKey() { | ||
42 | return impliedKey; | ||
43 | } | ||
44 | /** | ||
45 | * The implied instance tuple consists of the values in the implying tuple at these indices. | ||
46 | */ | ||
47 | public List<Integer> getImpliedIndices() { | ||
48 | return impliedIndices; | ||
49 | } | ||
50 | /** | ||
51 | * @param implyingKey instance tuples of this input key imply an instance tuple of the other key. | ||
52 | * @param impliedKey instance tuple of this input key is implied by the other key. | ||
53 | * @param implyingIndices the implied instance tuple consists of the values in the implying tuple at these indices. | ||
54 | */ | ||
55 | public InputKeyImplication(IInputKey implyingKey, IInputKey impliedKey, | ||
56 | List<Integer> implyingIndices) { | ||
57 | super(); | ||
58 | this.implyingKey = implyingKey; | ||
59 | this.impliedKey = impliedKey; | ||
60 | this.impliedIndices = Collections.unmodifiableList(new ArrayList<>(implyingIndices)); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public int hashCode() { | ||
65 | final int prime = 31; | ||
66 | int result = 1; | ||
67 | result = prime * result | ||
68 | + ((impliedIndices == null) ? 0 : impliedIndices.hashCode()); | ||
69 | result = prime * result | ||
70 | + ((impliedKey == null) ? 0 : impliedKey.hashCode()); | ||
71 | result = prime * result | ||
72 | + ((implyingKey == null) ? 0 : implyingKey.hashCode()); | ||
73 | return result; | ||
74 | } | ||
75 | @Override | ||
76 | public boolean equals(Object obj) { | ||
77 | if (this == obj) | ||
78 | return true; | ||
79 | if (obj == null) | ||
80 | return false; | ||
81 | if (!(obj instanceof InputKeyImplication)) | ||
82 | return false; | ||
83 | InputKeyImplication other = (InputKeyImplication) obj; | ||
84 | if (impliedIndices == null) { | ||
85 | if (other.impliedIndices != null) | ||
86 | return false; | ||
87 | } else if (!impliedIndices.equals(other.impliedIndices)) | ||
88 | return false; | ||
89 | if (impliedKey == null) { | ||
90 | if (other.impliedKey != null) | ||
91 | return false; | ||
92 | } else if (!impliedKey.equals(other.impliedKey)) | ||
93 | return false; | ||
94 | if (implyingKey == null) { | ||
95 | if (other.implyingKey != null) | ||
96 | return false; | ||
97 | } else if (!implyingKey.equals(other.implyingKey)) | ||
98 | return false; | ||
99 | return true; | ||
100 | } | ||
101 | |||
102 | |||
103 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java new file mode 100644 index 00000000..f9b05e5b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context.common; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | |||
13 | |||
14 | /** | ||
15 | * An input key that is identified by a single wrapped object and the class of the wrapper. | ||
16 | * @author Bergmann Gabor | ||
17 | * | ||
18 | */ | ||
19 | public abstract class BaseInputKeyWrapper<Wrapped> implements IInputKey { | ||
20 | protected Wrapped wrappedKey; | ||
21 | |||
22 | public BaseInputKeyWrapper(Wrapped wrappedKey) { | ||
23 | super(); | ||
24 | this.wrappedKey = wrappedKey; | ||
25 | } | ||
26 | |||
27 | public Wrapped getWrappedKey() { | ||
28 | return wrappedKey; | ||
29 | } | ||
30 | |||
31 | |||
32 | @Override | ||
33 | public int hashCode() { | ||
34 | return ((wrappedKey == null) ? 0 : wrappedKey.hashCode()); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public boolean equals(Object obj) { | ||
39 | if (this == obj) | ||
40 | return true; | ||
41 | if (obj == null) | ||
42 | return false; | ||
43 | if (!(this.getClass().equals(obj.getClass()))) | ||
44 | return false; | ||
45 | BaseInputKeyWrapper other = (BaseInputKeyWrapper) obj; | ||
46 | if (wrappedKey == null) { | ||
47 | if (other.wrappedKey != null) | ||
48 | return false; | ||
49 | } else if (!wrappedKey.equals(other.wrappedKey)) | ||
50 | return false; | ||
51 | return true; | ||
52 | } | ||
53 | |||
54 | |||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java new file mode 100644 index 00000000..eb972c2d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java | |||
@@ -0,0 +1,165 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.context.common; | ||
10 | |||
11 | |||
12 | |||
13 | /** | ||
14 | * Instance tuples are of form (x), where object x is an instance of the given Java class or its subclasses. | ||
15 | * <p> Fine print 1: classes with the same name are considered equivalent. | ||
16 | * Can be instantiated with class name, even if the class itself is not loaded yet; but if the class is available, passing it in the constructor is beneficial to avoid classloading troubles. | ||
17 | * <p> Fine print 2: primitive types (char, etc.) are transparently treated as their wrapper class (Character, etc.). | ||
18 | * <p> Non-enumerable type, can only be checked. | ||
19 | * <p> Stateless type (objects can't change their type) | ||
20 | * @author Bergmann Gabor | ||
21 | * | ||
22 | */ | ||
23 | public class JavaTransitiveInstancesKey extends BaseInputKeyWrapper<String> { | ||
24 | |||
25 | /** | ||
26 | * The actual Class whose (transitive) instances this relation contains. Can be null at compile time, if only the name is available. | ||
27 | * Can be a primitive. | ||
28 | */ | ||
29 | private Class<?> cachedOriginalInstanceClass; | ||
30 | |||
31 | /** | ||
32 | * Same as {@link #cachedOriginalInstanceClass}, but primitive classes are replaced with their wrapper classes (e.g. int --> java.lang.Integer). | ||
33 | */ | ||
34 | private Class<?> cachedWrapperInstanceClass; | ||
35 | |||
36 | /** | ||
37 | * Preferred constructor. | ||
38 | */ | ||
39 | public JavaTransitiveInstancesKey(Class<?> instanceClass) { | ||
40 | this(primitiveTypeToWrapperClass(instanceClass).getName()); | ||
41 | this.cachedOriginalInstanceClass = instanceClass; | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Call this constructor only in contexts where the class itself is not available for loading, e.g. it has not yet been compiled. | ||
46 | */ | ||
47 | public JavaTransitiveInstancesKey(String className) { | ||
48 | super(className); | ||
49 | } | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Returns null if class cannot be loaded. | ||
54 | */ | ||
55 | private Class<?> getOriginalInstanceClass() { | ||
56 | if (cachedOriginalInstanceClass == null) { | ||
57 | try { | ||
58 | resolveClassInternal(); | ||
59 | } catch (ClassNotFoundException e) { | ||
60 | // class not yet available at this point | ||
61 | } | ||
62 | } | ||
63 | return cachedOriginalInstanceClass; | ||
64 | } | ||
65 | |||
66 | |||
67 | /** | ||
68 | * @return non-null instance class | ||
69 | * @throws ClassNotFoundException | ||
70 | */ | ||
71 | private Class<?> forceGetOriginalInstanceClass() throws ClassNotFoundException { | ||
72 | if (cachedOriginalInstanceClass == null) { | ||
73 | resolveClassInternal(); | ||
74 | } | ||
75 | return cachedOriginalInstanceClass; | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * @return non-null instance class, wrapped if primitive class | ||
80 | * @throws ClassNotFoundException | ||
81 | */ | ||
82 | public Class<?> forceGetWrapperInstanceClass() throws ClassNotFoundException { | ||
83 | forceGetOriginalInstanceClass(); | ||
84 | return getWrapperInstanceClass(); | ||
85 | } | ||
86 | /** | ||
87 | * @return non-null instance class, wrapped if primitive class | ||
88 | * @throws ClassNotFoundException | ||
89 | */ | ||
90 | public Class<?> forceGetInstanceClass() throws ClassNotFoundException { | ||
91 | return forceGetWrapperInstanceClass(); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * @return instance class, wrapped if primitive class, null if class cannot be loaded | ||
96 | */ | ||
97 | public Class<?> getWrapperInstanceClass() { | ||
98 | if (cachedWrapperInstanceClass == null) { | ||
99 | cachedWrapperInstanceClass = primitiveTypeToWrapperClass(getOriginalInstanceClass()); | ||
100 | } | ||
101 | return cachedWrapperInstanceClass; | ||
102 | } | ||
103 | /** | ||
104 | * @return instance class, wrapped if primitive class, null if class cannot be loaded | ||
105 | */ | ||
106 | public Class<?> getInstanceClass() { | ||
107 | return getWrapperInstanceClass(); | ||
108 | } | ||
109 | |||
110 | private void resolveClassInternal() throws ClassNotFoundException { | ||
111 | cachedOriginalInstanceClass = Class.forName(wrappedKey); | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public String getPrettyPrintableName() { | ||
116 | getWrapperInstanceClass(); | ||
117 | return cachedWrapperInstanceClass == null ? wrappedKey == null ? "<null>" : wrappedKey : cachedWrapperInstanceClass.getName(); | ||
118 | } | ||
119 | |||
120 | @Override | ||
121 | public String getStringID() { | ||
122 | return "javaClass#"+ getPrettyPrintableName(); | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | public int getArity() { | ||
127 | return 1; | ||
128 | } | ||
129 | |||
130 | @Override | ||
131 | public boolean isEnumerable() { | ||
132 | return false; | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public String toString() { | ||
137 | return this.getPrettyPrintableName(); | ||
138 | } | ||
139 | |||
140 | private static Class<?> primitiveTypeToWrapperClass(Class<?> instanceClass) { | ||
141 | if (instanceClass != null && instanceClass.isPrimitive()) { | ||
142 | if (Void.TYPE.equals(instanceClass)) | ||
143 | return Void.class; | ||
144 | if (Boolean.TYPE.equals(instanceClass)) | ||
145 | return Boolean.class; | ||
146 | if (Character.TYPE.equals(instanceClass)) | ||
147 | return Character.class; | ||
148 | if (Byte.TYPE.equals(instanceClass)) | ||
149 | return Byte.class; | ||
150 | if (Short.TYPE.equals(instanceClass)) | ||
151 | return Short.class; | ||
152 | if (Integer.TYPE.equals(instanceClass)) | ||
153 | return Integer.class; | ||
154 | if (Long.TYPE.equals(instanceClass)) | ||
155 | return Long.class; | ||
156 | if (Float.TYPE.equals(instanceClass)) | ||
157 | return Float.class; | ||
158 | if (Double.TYPE.equals(instanceClass)) | ||
159 | return Double.class; | ||
160 | } | ||
161 | return instanceClass; | ||
162 | } | ||
163 | |||
164 | |||
165 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java new file mode 100644 index 00000000..bb24ec8c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java | |||
@@ -0,0 +1,153 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Abel Hegedus, 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.context.surrogate; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.NoSuchElementException; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IProvider; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider; | ||
23 | |||
24 | /** | ||
25 | * @author Abel Hegedus | ||
26 | * | ||
27 | */ | ||
28 | public class SurrogateQueryRegistry { | ||
29 | |||
30 | private Map<IInputKey, IProvider<PQuery>> registeredSurrogateQueryMap = new HashMap<>(); | ||
31 | private Map<IInputKey, IProvider<PQuery>> dynamicSurrogateQueryMap = new HashMap<>(); | ||
32 | |||
33 | /** | ||
34 | * Hidden constructor | ||
35 | */ | ||
36 | private SurrogateQueryRegistry() { | ||
37 | } | ||
38 | |||
39 | private static final SurrogateQueryRegistry INSTANCE = new SurrogateQueryRegistry(); | ||
40 | |||
41 | public static SurrogateQueryRegistry instance() { | ||
42 | return INSTANCE; | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * | ||
47 | * @param feature | ||
48 | * @param surrogateQuery | ||
49 | * @return the previous surrogate query associated with feature, or null if there was no such query FQN registered | ||
50 | * @throws IllegalArgumentException if feature or surrogateQuery is null | ||
51 | */ | ||
52 | public IProvider<PQuery> registerSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { | ||
53 | Preconditions.checkArgument(surrogateQuery != null, "Surrogate query must not be null!"); | ||
54 | return registerSurrogateQueryForFeature(feature, new SingletonInstanceProvider<PQuery>(surrogateQuery)); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * | ||
59 | * @param feature | ||
60 | * @param surrogateQuery | ||
61 | * @return the previous surrogate query associated with feature, or null | ||
62 | * if there was no such query registered | ||
63 | * @throws IllegalArgumentException | ||
64 | * if feature or surrogateQuery is null | ||
65 | */ | ||
66 | public IProvider<PQuery> registerSurrogateQueryForFeature(IInputKey feature, IProvider<PQuery> surrogateQueryProvider) { | ||
67 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
68 | Preconditions.checkArgument(surrogateQueryProvider != null, "Surrogate query must not be null!"); | ||
69 | return registeredSurrogateQueryMap.put(feature, surrogateQueryProvider); | ||
70 | } | ||
71 | |||
72 | public IProvider<PQuery> addDynamicSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { | ||
73 | Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); | ||
74 | return addDynamicSurrogateQueryForFeature(feature, new SingletonInstanceProvider<PQuery>(surrogateQuery)); | ||
75 | } | ||
76 | |||
77 | public IProvider<PQuery> addDynamicSurrogateQueryForFeature(IInputKey feature, IProvider<PQuery> surrogateQuery) { | ||
78 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
79 | Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); | ||
80 | return dynamicSurrogateQueryMap.put(feature, surrogateQuery); | ||
81 | } | ||
82 | |||
83 | public IProvider<PQuery> removeDynamicSurrogateQueryForFeature(IInputKey feature) { | ||
84 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
85 | return dynamicSurrogateQueryMap.remove(feature); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * | ||
90 | * @param feature that may have surrogate query defined, null not allowed | ||
91 | * @return true if the feature has a surrogate query defined | ||
92 | * @throws IllegalArgumentException if feature is null | ||
93 | */ | ||
94 | public boolean hasSurrogateQueryFQN(IInputKey feature) { | ||
95 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
96 | boolean surrogateExists = dynamicSurrogateQueryMap.containsKey(feature); | ||
97 | if(!surrogateExists){ | ||
98 | surrogateExists = registeredSurrogateQueryMap.containsKey(feature); | ||
99 | } | ||
100 | return surrogateExists; | ||
101 | } | ||
102 | |||
103 | /** | ||
104 | * | ||
105 | * @param feature for which the surrogate query FQN should be returned | ||
106 | * @return the surrogate query FQN defined for the feature | ||
107 | * @throws IllegalArgumentException if feature is null | ||
108 | * @throws NoSuchElementException if the feature has no surrogate query defined, use {@link #hasSurrogateQueryFQN} to check | ||
109 | */ | ||
110 | public PQuery getSurrogateQuery(IInputKey feature) { | ||
111 | Preconditions.checkArgument(feature != null, "Feature must not be null!"); | ||
112 | IProvider<PQuery> surrogate = dynamicSurrogateQueryMap.get(feature); | ||
113 | if(surrogate == null) { | ||
114 | surrogate = registeredSurrogateQueryMap.get(feature); | ||
115 | } | ||
116 | if(surrogate != null) { | ||
117 | return surrogate.get(); | ||
118 | } else { | ||
119 | throw new NoSuchElementException(String.format("Feature %s has no surrogate query defined! Use #hasSurrogateQueryFQN to check existence.", feature)); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * @return an unmodifiable set of features with registered surrogate queries | ||
125 | */ | ||
126 | public Set<IInputKey> getRegisteredSurrogateQueries() { | ||
127 | return Collections.unmodifiableSet(getRegisteredSurrogateQueriesInternal()); | ||
128 | } | ||
129 | |||
130 | private Set<IInputKey> getRegisteredSurrogateQueriesInternal() { | ||
131 | return registeredSurrogateQueryMap.keySet(); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * @return an unmodifiable set of features with dynamically added surrogate queries | ||
136 | */ | ||
137 | public Set<IInputKey> getDynamicSurrogateQueries() { | ||
138 | return Collections.unmodifiableSet(getDynamicSurrogateQueriesInternal()); | ||
139 | } | ||
140 | |||
141 | private Set<IInputKey> getDynamicSurrogateQueriesInternal() { | ||
142 | return dynamicSurrogateQueryMap.keySet(); | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * @return an unmodifiable set that contains all features with surrogate queries. | ||
147 | */ | ||
148 | public Set<IInputKey> getAllSurrogateQueries() { | ||
149 | Set<IInputKey> results = new HashSet<>(getRegisteredSurrogateQueriesInternal()); | ||
150 | results.addAll(getDynamicSurrogateQueriesInternal()); | ||
151 | return results; | ||
152 | } | ||
153 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java new file mode 100644 index 00000000..66587f77 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java | |||
@@ -0,0 +1,58 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.IMemory; | ||
21 | |||
22 | /** | ||
23 | * Common parts of nullary and identity specializations. | ||
24 | * | ||
25 | * @noextend This class is not intended to be subclassed by clients. | ||
26 | * @author Gabor Bergmann | ||
27 | * @since 2.0 | ||
28 | */ | ||
29 | abstract class AbstractTrivialMaskedMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> { | ||
30 | |||
31 | protected IMemory<Tuple> tuples; | ||
32 | |||
33 | protected AbstractTrivialMaskedMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
34 | super(mask, owner); | ||
35 | tuples = CollectionsFactory.createMemory(Object.class, bucketType); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) { | ||
40 | throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void clear() { | ||
45 | tuples.clear(); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public int getTotalSize() { | ||
50 | return tuples.size(); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Iterator<Tuple> iterator() { | ||
55 | return tuples.iterator(); | ||
56 | } | ||
57 | |||
58 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java new file mode 100644 index 00000000..92081409 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java | |||
@@ -0,0 +1,127 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.memories; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
25 | |||
26 | /** | ||
27 | * @author Gabor Bergmann | ||
28 | * | ||
29 | * Default implementation that covers all cases. | ||
30 | * | ||
31 | * @since 2.0 | ||
32 | */ | ||
33 | public final class DefaultMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
34 | extends MaskedTupleMemory<Timestamp> { | ||
35 | /** | ||
36 | * Maps a signature tuple to the bucket of tuples with the given signature. | ||
37 | * | ||
38 | * @since 2.0 | ||
39 | */ | ||
40 | protected IMultiLookup<Tuple, Tuple> signatureToTuples; | ||
41 | |||
42 | /** | ||
43 | * @param mask | ||
44 | * The mask used to index the matchings | ||
45 | * @param owner | ||
46 | * the object "owning" this memory | ||
47 | * @param bucketType | ||
48 | * the kind of tuple collection maintained for each indexer bucket | ||
49 | * @since 2.0 | ||
50 | */ | ||
51 | public DefaultMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
52 | super(mask, owner); | ||
53 | signatureToTuples = CollectionsFactory.<Tuple, Tuple> createMultiLookup(Object.class, bucketType, Object.class); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public boolean add(Tuple tuple) { | ||
58 | Tuple signature = mask.transform(tuple); | ||
59 | return add(tuple, signature); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public boolean add(Tuple tuple, Tuple signature) { | ||
64 | try { | ||
65 | return signatureToTuples.addPair(signature, tuple) == ChangeGranularity.KEY; | ||
66 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
67 | throw raiseDuplicateInsertion(tuple); | ||
68 | } | ||
69 | |||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean remove(Tuple tuple) { | ||
74 | Tuple signature = mask.transform(tuple); | ||
75 | return remove(tuple, signature); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public boolean remove(Tuple tuple, Tuple signature) { | ||
80 | try { | ||
81 | return signatureToTuples.removePair(signature, tuple) == ChangeGranularity.KEY; | ||
82 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
83 | throw raiseDuplicateDeletion(tuple); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) { | ||
89 | throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public Collection<Tuple> get(ITuple signature) { | ||
94 | IMemoryView<Tuple> bucket = signatureToTuples.lookupUnsafe(signature); | ||
95 | return bucket == null ? null : bucket.distinctValues(); | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public void clear() { | ||
100 | signatureToTuples.clear(); | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Iterable<Tuple> getSignatures() { | ||
105 | return signatureToTuples.distinctKeys(); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public Iterator<Tuple> iterator() { | ||
110 | return signatureToTuples.distinctValues().iterator(); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public int getTotalSize() { | ||
115 | int i = 0; | ||
116 | for (Tuple key : signatureToTuples.distinctKeys()) { | ||
117 | i += signatureToTuples.lookup(key).size(); | ||
118 | } | ||
119 | return i; | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public int getKeysetSize() { | ||
124 | return signatureToTuples.countKeys(); | ||
125 | } | ||
126 | |||
127 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java new file mode 100644 index 00000000..dc59daf5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java | |||
@@ -0,0 +1,77 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
18 | |||
19 | /** | ||
20 | * Specialized for identity mask; tuples are stored as a simple set/multiset memory. | ||
21 | * | ||
22 | * @author Gabor Bergmann | ||
23 | * @since 2.0 | ||
24 | */ | ||
25 | public final class IdentityMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends AbstractTrivialMaskedMemory<Timestamp> { | ||
26 | |||
27 | /** | ||
28 | * @param mask | ||
29 | * The mask used to index the matchings | ||
30 | * @param owner the object "owning" this memory | ||
31 | * @param bucketType the kind of tuple collection maintained for each indexer bucket | ||
32 | * @since 2.0 | ||
33 | */ | ||
34 | public IdentityMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
35 | super(mask, bucketType, owner); | ||
36 | if (!mask.isIdentity()) throw new IllegalArgumentException(mask.toString()); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public int getKeysetSize() { | ||
41 | return tuples.size(); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Iterable<Tuple> getSignatures() { | ||
46 | return tuples; | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public Collection<Tuple> get(ITuple signature) { | ||
51 | Tuple contained = tuples.theContainedVersionOfUnsafe(signature); | ||
52 | return contained != null ? | ||
53 | Collections.singleton(contained) : | ||
54 | null; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public boolean remove(Tuple tuple, Tuple signature) { | ||
59 | return tuples.removeOne(tuple); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public boolean remove(Tuple tuple) { | ||
64 | return tuples.removeOne(tuple); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean add(Tuple tuple, Tuple signature) { | ||
69 | return tuples.addOne(tuple); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean add(Tuple tuple) { | ||
74 | return tuples.addOne(tuple); | ||
75 | } | ||
76 | |||
77 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java new file mode 100644 index 00000000..62377624 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java | |||
@@ -0,0 +1,385 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyDefaultMaskedTupleMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyIdentityMaskedTupleMemory; | ||
18 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyNullaryMaskedTupleMemory; | ||
19 | import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyUnaryMaskedTupleMemory; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.resumable.MaskedResumable; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
28 | |||
29 | /** | ||
30 | * Indexes a collection of tuples by their signature (i.e. footprint, projection) obtained according to a mask. May | ||
31 | * belong to an "owner" (for documentation / traceability purposes). | ||
32 | * <p> | ||
33 | * There are timeless and timely versions of the different memories. Timely versions associate {@link Timeline}s with | ||
34 | * the stored tuples. | ||
35 | * | ||
36 | * @noextend This class is not intended to be subclassed by clients. | ||
37 | * @author Gabor Bergmann | ||
38 | * @author Tamas Szabo | ||
39 | * @since 2.0 | ||
40 | */ | ||
41 | public abstract class MaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
42 | implements Clearable, MaskedResumable<Timestamp> { | ||
43 | |||
44 | /** | ||
45 | * Creates a new memory for the given owner that indexes tuples according to the given mask. | ||
46 | */ | ||
47 | public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask, | ||
48 | final MemoryType bucketType, final Object owner) { | ||
49 | return create(mask, bucketType, owner, false); | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if | ||
54 | * the created memory should be timely or not. <br> | ||
55 | * <br> | ||
56 | * Timely means that tuples are associated with a timeline. | ||
57 | * | ||
58 | * @since 2.3 | ||
59 | */ | ||
60 | public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask, | ||
61 | final MemoryType bucketType, final Object owner, final boolean isTimely) { | ||
62 | return create(mask, bucketType, owner, isTimely, false); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if | ||
67 | * the created memory should be timely or not. In case of timely memory, clients can also specify if the memory is | ||
68 | * lazy or not. <br> | ||
69 | * <br> | ||
70 | * Timely means that tuples are associated with a timeline. <br> | ||
71 | * <br> | ||
72 | * Lazyness can only be used together with timely memories. It means that the maintenance of the timelines is lazy, | ||
73 | * that is, the memory only updates its internal data structures at the timestamp affected by an update, and can be | ||
74 | * instructed later to resume the maintenance at higher timestamps, as well. | ||
75 | * | ||
76 | * @since 2.4 | ||
77 | */ | ||
78 | public static <T extends Comparable<T>> MaskedTupleMemory<T> create(final TupleMask mask, | ||
79 | final MemoryType bucketType, final Object owner, final boolean isTimely, final boolean isLazy) { | ||
80 | if (isTimely) { | ||
81 | if (bucketType != MemoryType.SETS) { | ||
82 | throw new IllegalArgumentException("Timely memories only support SETS as the bucket type!"); | ||
83 | } | ||
84 | if (mask.isIdentity()) { | ||
85 | return new TimelyIdentityMaskedTupleMemory<T>(mask, owner, isLazy); | ||
86 | } else if (0 == mask.getSize()) { | ||
87 | return new TimelyNullaryMaskedTupleMemory<T>(mask, owner, isLazy); | ||
88 | } else if (1 == mask.getSize()) { | ||
89 | return new TimelyUnaryMaskedTupleMemory<T>(mask, owner, isLazy); | ||
90 | } else { | ||
91 | return new TimelyDefaultMaskedTupleMemory<T>(mask, owner, isLazy); | ||
92 | } | ||
93 | } else { | ||
94 | if (isLazy) { | ||
95 | throw new IllegalArgumentException("Lazy maintenance is only supported by timely memories!"); | ||
96 | } | ||
97 | if (mask.isIdentity()) { | ||
98 | return new IdentityMaskedTupleMemory<T>(mask, bucketType, owner); | ||
99 | } else if (0 == mask.getSize()) { | ||
100 | return new NullaryMaskedTupleMemory<T>(mask, bucketType, owner); | ||
101 | } else if (1 == mask.getSize()) { | ||
102 | return new UnaryMaskedTupleMemory<T>(mask, bucketType, owner); | ||
103 | } else { | ||
104 | return new DefaultMaskedTupleMemory<T>(mask, bucketType, owner); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
111 | throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public Iterable<Tuple> getResumableSignatures() { | ||
116 | throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public Timestamp getResumableTimestamp() { | ||
121 | return null; | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Initializes the contents of this memory based on the contents of another memory. The default value is associated | ||
126 | * with each tuple in the timely memories. | ||
127 | * | ||
128 | * @since 2.3 | ||
129 | */ | ||
130 | public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) { | ||
131 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Returns true if there is any tuple with the given signature that is present at the timestamp +inf, false | ||
136 | * otherwise. | ||
137 | * @since 2.4 | ||
138 | */ | ||
139 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
140 | return get(signature) != null; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Returns true of this memory is timely, false otherwise. | ||
145 | * | ||
146 | * @since 2.3 | ||
147 | */ | ||
148 | public boolean isTimely() { | ||
149 | return false; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * The mask by which the tuples are indexed. | ||
154 | */ | ||
155 | protected final TupleMask mask; | ||
156 | |||
157 | /** | ||
158 | * The object "owning" this memory. May be null. | ||
159 | * | ||
160 | * @since 1.7 | ||
161 | */ | ||
162 | protected final Object owner; | ||
163 | |||
164 | /** | ||
165 | * The node owning this memory. May be null. | ||
166 | * | ||
167 | * @since 2.0 | ||
168 | */ | ||
169 | public Object getOwner() { | ||
170 | return owner; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * The mask according to which tuples are projected and indexed. | ||
175 | * | ||
176 | * @since 2.0 | ||
177 | */ | ||
178 | public TupleMask getMask() { | ||
179 | return mask; | ||
180 | } | ||
181 | |||
182 | /** | ||
183 | * @return the number of distinct signatures of all stored tuples. | ||
184 | */ | ||
185 | public abstract int getKeysetSize(); | ||
186 | |||
187 | /** | ||
188 | * @return the total number of distinct tuples stored. Multiple copies of the same tuple, if allowed, are counted as | ||
189 | * one. | ||
190 | * | ||
191 | * <p> | ||
192 | * This is currently not cached but computed on demand. It is therefore not efficient, and shall only be | ||
193 | * used for debug / profiling purposes. | ||
194 | */ | ||
195 | public abstract int getTotalSize(); | ||
196 | |||
197 | /** | ||
198 | * Iterates over distinct tuples stored in the memory, regardless of their signatures. | ||
199 | */ | ||
200 | public abstract Iterator<Tuple> iterator(); | ||
201 | |||
202 | /** | ||
203 | * Retrieves a read-only view of exactly those signatures for which at least one tuple is stored | ||
204 | * | ||
205 | * @since 2.0 | ||
206 | */ | ||
207 | public abstract Iterable<Tuple> getSignatures(); | ||
208 | |||
209 | /** | ||
210 | * Retrieves tuples that have the specified signature | ||
211 | * | ||
212 | * @return collection of tuples found, null if none | ||
213 | */ | ||
214 | public abstract Collection<Tuple> get(final ITuple signature); | ||
215 | |||
216 | /** | ||
217 | * Retrieves the tuples and their associated timelines that have the specified signature. | ||
218 | * | ||
219 | * @return the mappings from tuples to timelines, null if there is no mapping for the signature | ||
220 | * @since 2.4 | ||
221 | */ | ||
222 | public abstract Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature); | ||
223 | |||
224 | /** | ||
225 | * Retrieves tuples that have the specified signature. | ||
226 | * | ||
227 | * @return collection of tuples found, never null | ||
228 | * @since 2.1 | ||
229 | */ | ||
230 | public Collection<Tuple> getOrEmpty(final ITuple signature) { | ||
231 | final Collection<Tuple> result = get(signature); | ||
232 | return result == null ? Collections.emptySet() : result; | ||
233 | } | ||
234 | |||
235 | /** | ||
236 | * Retrieves tuples with their associated timelines that have the specified signature. | ||
237 | * | ||
238 | * @return map of tuples and timelines found, never null | ||
239 | * @since 2.4 | ||
240 | */ | ||
241 | public Map<Tuple, Timeline<Timestamp>> getOrEmptyWithTimeline(final ITuple signature) { | ||
242 | final Map<Tuple, Timeline<Timestamp>> result = getWithTimeline(signature); | ||
243 | return result == null ? Collections.emptyMap() : result; | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * Removes a tuple occurrence from the memory with the given signature. | ||
248 | * | ||
249 | * @param tuple | ||
250 | * the tuple to be removed from the memory | ||
251 | * @param signature | ||
252 | * precomputed footprint of the tuple according to the mask | ||
253 | * | ||
254 | * @return true if this was the the last occurrence of the signature (according to the mask) | ||
255 | */ | ||
256 | public boolean remove(final Tuple tuple, final Tuple signature) { | ||
257 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
258 | } | ||
259 | |||
260 | /** | ||
261 | * Removes a tuple occurrence from the memory with the given signature and timestamp. | ||
262 | * | ||
263 | * @param tuple | ||
264 | * the tuple to be removed from the memory | ||
265 | * @param signature | ||
266 | * precomputed footprint of the tuple according to the mask | ||
267 | * @param timestamp | ||
268 | * the timestamp associated with the tuple | ||
269 | * | ||
270 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
271 | * | ||
272 | * @since 2.4 | ||
273 | */ | ||
274 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
275 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * Removes a tuple occurrence from the memory. | ||
280 | * | ||
281 | * @param tuple | ||
282 | * the tuple to be removed from the memory | ||
283 | * | ||
284 | * @return true if this was the the last occurrence of the signature (according to the mask) | ||
285 | */ | ||
286 | public boolean remove(final Tuple tuple) { | ||
287 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
288 | } | ||
289 | |||
290 | /** | ||
291 | * Removes a tuple occurrence from the memory with the given timestamp. | ||
292 | * | ||
293 | * @param tuple | ||
294 | * the tuple to be removed from the memory | ||
295 | * @param timestamp | ||
296 | * the timestamp associated with the tuple | ||
297 | * | ||
298 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
299 | * | ||
300 | * @since 2.4 | ||
301 | */ | ||
302 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
303 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * Adds a tuple occurrence to the memory with the given signature. | ||
308 | * | ||
309 | * @param tuple | ||
310 | * the tuple to be added to the memory | ||
311 | * @param signature | ||
312 | * precomputed footprint of the tuple according to the mask | ||
313 | * | ||
314 | * @return true if new signature encountered (according to the mask) | ||
315 | */ | ||
316 | public boolean add(final Tuple tuple, final Tuple signature) { | ||
317 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * Adds a tuple occurrence to the memory with the given signature and timestamp. | ||
322 | * | ||
323 | * @param tuple | ||
324 | * the tuple to be added to the memory | ||
325 | * @param signature | ||
326 | * precomputed footprint of the tuple according to the mask | ||
327 | * @param timestamp | ||
328 | * the timestamp associated with the tuple | ||
329 | * | ||
330 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
331 | * | ||
332 | * @since 2.4 | ||
333 | */ | ||
334 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
335 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * Adds a tuple occurrence to the memory. | ||
340 | * | ||
341 | * @param tuple | ||
342 | * the tuple to be added to the memory | ||
343 | * | ||
344 | * @return true if new signature encountered (according to the mask) | ||
345 | */ | ||
346 | public boolean add(final Tuple tuple) { | ||
347 | throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * Adds a tuple occurrence to the memory with the given timestamp. | ||
352 | * | ||
353 | * @param tuple | ||
354 | * the tuple to be added to the memory | ||
355 | * @param timestamp | ||
356 | * the timestamp associated with the tuple | ||
357 | * | ||
358 | * @return A {@link Diff} describing how the timeline of the given tuple changed. | ||
359 | * | ||
360 | * @since 2.4 | ||
361 | */ | ||
362 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
363 | throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); | ||
364 | } | ||
365 | |||
366 | protected MaskedTupleMemory(final TupleMask mask, final Object owner) { | ||
367 | super(); | ||
368 | this.mask = mask; | ||
369 | this.owner = owner; | ||
370 | } | ||
371 | |||
372 | protected IllegalStateException raiseDuplicateInsertion(final Tuple tuple) { | ||
373 | return new IllegalStateException(String.format("Duplicate insertion of tuple %s into %s", tuple, owner)); | ||
374 | } | ||
375 | |||
376 | protected IllegalStateException raiseDuplicateDeletion(final Tuple tuple) { | ||
377 | return new IllegalStateException(String.format("Duplicate deletion of tuple %s from %s", tuple, owner)); | ||
378 | } | ||
379 | |||
380 | @Override | ||
381 | public String toString() { | ||
382 | return getClass().getSimpleName() + "<" + mask + ">@" + owner; | ||
383 | } | ||
384 | |||
385 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java new file mode 100644 index 00000000..7fa9e053 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java | |||
@@ -0,0 +1,85 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
20 | |||
21 | /** | ||
22 | * Specialized for nullary mask; tuples are stored as a simple set/multiset memory. | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | * @since 2.0 | ||
26 | */ | ||
27 | public final class NullaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends AbstractTrivialMaskedMemory<Timestamp> { | ||
28 | |||
29 | protected static final Set<Tuple> UNIT_RELATION = | ||
30 | Collections.singleton(Tuples.staticArityFlatTupleOf()); | ||
31 | protected static final Set<Tuple> EMPTY_RELATION = | ||
32 | Collections.emptySet(); | ||
33 | /** | ||
34 | * @param mask | ||
35 | * The mask used to index the matchings | ||
36 | * @param owner the object "owning" this memory | ||
37 | * @param bucketType the kind of tuple collection maintained for each indexer bucket | ||
38 | * @since 2.0 | ||
39 | */ | ||
40 | public NullaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
41 | super(mask, bucketType, owner); | ||
42 | if (0 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getKeysetSize() { | ||
47 | return tuples.isEmpty() ? 0 : 1; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public Iterable<Tuple> getSignatures() { | ||
52 | return tuples.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public Collection<Tuple> get(ITuple signature) { | ||
57 | if (0 == signature.getSize()) | ||
58 | return tuples.distinctValues(); | ||
59 | else return null; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public boolean remove(Tuple tuple, Tuple signature) { | ||
64 | tuples.removeOne(tuple); | ||
65 | return tuples.isEmpty(); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public boolean remove(Tuple tuple) { | ||
70 | return remove(tuple, null); | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public boolean add(Tuple tuple, Tuple signature) { | ||
75 | boolean wasEmpty = tuples.isEmpty(); | ||
76 | tuples.addOne(tuple); | ||
77 | return wasEmpty; | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public boolean add(Tuple tuple) { | ||
82 | return add(tuple, null); | ||
83 | } | ||
84 | |||
85 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java new file mode 100644 index 00000000..f34cc9e3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java | |||
@@ -0,0 +1,143 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
25 | |||
26 | /** | ||
27 | * Specialized for unary mask; tuples are indexed by a single column as opposed to a projection (signature) tuple. | ||
28 | * | ||
29 | * @author Gabor Bergmann | ||
30 | * @since 2.0 | ||
31 | */ | ||
32 | public final class UnaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> { | ||
33 | |||
34 | protected IMultiLookup<Object, Tuple> columnToTuples; | ||
35 | protected final int keyPosition; | ||
36 | |||
37 | /** | ||
38 | * @param mask | ||
39 | * The mask used to index the matchings | ||
40 | * @param owner the object "owning" this memory | ||
41 | * @param bucketType the kind of tuple collection maintained for each indexer bucket | ||
42 | * @since 2.0 | ||
43 | */ | ||
44 | public UnaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { | ||
45 | super(mask, owner); | ||
46 | if (1 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); | ||
47 | |||
48 | columnToTuples = CollectionsFactory.<Object, Tuple>createMultiLookup( | ||
49 | Object.class, bucketType, Object.class); | ||
50 | keyPosition = mask.indices[0]; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public void clear() { | ||
55 | columnToTuples.clear(); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public int getKeysetSize() { | ||
60 | return columnToTuples.countKeys(); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public int getTotalSize() { | ||
65 | int i = 0; | ||
66 | for (Object key : columnToTuples.distinctKeys()) { | ||
67 | i += columnToTuples.lookup(key).size(); | ||
68 | } | ||
69 | return i; | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public Iterator<Tuple> iterator() { | ||
74 | return columnToTuples.distinctValues().iterator(); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public Iterable<Tuple> getSignatures() { | ||
79 | return () -> { | ||
80 | Iterator<Object> wrapped = columnToTuples.distinctKeys().iterator(); | ||
81 | return new Iterator<Tuple>() { | ||
82 | @Override | ||
83 | public boolean hasNext() { | ||
84 | return wrapped.hasNext(); | ||
85 | } | ||
86 | @Override | ||
87 | public Tuple next() { | ||
88 | Object key = wrapped.next(); | ||
89 | return Tuples.staticArityFlatTupleOf(key); | ||
90 | } | ||
91 | }; | ||
92 | }; | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public Collection<Tuple> get(ITuple signature) { | ||
97 | Object key = signature.get(0); | ||
98 | IMemoryView<Tuple> bucket = columnToTuples.lookup(key); | ||
99 | return bucket == null ? null : bucket.distinctValues(); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(ITuple signature) { | ||
104 | throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); | ||
105 | } | ||
106 | |||
107 | @Override | ||
108 | public boolean remove(Tuple tuple, Tuple signature) { | ||
109 | return removeInternal(tuple, tuple.get(keyPosition)); | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public boolean remove(Tuple tuple) { | ||
114 | return removeInternal(tuple, tuple.get(keyPosition)); | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public boolean add(Tuple tuple, Tuple signature) { | ||
119 | return addInternal(tuple, tuple.get(keyPosition)); | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public boolean add(Tuple tuple) { | ||
124 | return addInternal(tuple, tuple.get(keyPosition)); | ||
125 | } | ||
126 | |||
127 | protected boolean addInternal(Tuple tuple, Object key) { | ||
128 | try { | ||
129 | return columnToTuples.addPair(key, tuple) == ChangeGranularity.KEY; | ||
130 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
131 | throw raiseDuplicateInsertion(tuple); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | protected boolean removeInternal(Tuple tuple, Object key) { | ||
136 | try { | ||
137 | return columnToTuples.removePair(key, tuple) == ChangeGranularity.KEY; | ||
138 | } catch (IllegalStateException ex) { // ignore worthless internal exception details | ||
139 | throw raiseDuplicateDeletion(tuple); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java new file mode 100644 index 00000000..45ce3a4e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java | |||
@@ -0,0 +1,228 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Set; | ||
17 | import java.util.TreeMap; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
28 | |||
29 | /** | ||
30 | * Common parts of timely default and timely unary implementations. | ||
31 | * | ||
32 | * @noextend This class is not intended to be subclassed by clients. | ||
33 | * @author Tamas Szabo | ||
34 | * @since 2.3 | ||
35 | */ | ||
36 | abstract class AbstractTimelyMaskedMemory<Timestamp extends Comparable<Timestamp>, KeyType> | ||
37 | extends MaskedTupleMemory<Timestamp> { | ||
38 | |||
39 | protected final TreeMap<Timestamp, Set<KeyType>> foldingStates; | ||
40 | protected final Map<KeyType, TimelyMemory<Timestamp>> memoryMap; | ||
41 | protected final boolean isLazy; | ||
42 | |||
43 | public AbstractTimelyMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
44 | super(mask, owner); | ||
45 | this.isLazy = isLazy; | ||
46 | this.memoryMap = CollectionsFactory.createMap(); | ||
47 | this.foldingStates = this.isLazy ? CollectionsFactory.createTreeMap() : null; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) { | ||
52 | final Iterable<Tuple> signatures = other.getSignatures(); | ||
53 | for (final Tuple signature : signatures) { | ||
54 | if (other.isTimely()) { | ||
55 | final Map<Tuple, Timeline<Timestamp>> tupleMap = other.getWithTimeline(signature); | ||
56 | for (final Entry<Tuple, Timeline<Timestamp>> entry : tupleMap.entrySet()) { | ||
57 | for (final Signed<Timestamp> signed : entry.getValue().asChangeSequence()) { | ||
58 | if (signed.getDirection() == Direction.DELETE) { | ||
59 | this.removeWithTimestamp(entry.getKey(), signed.getPayload()); | ||
60 | } else { | ||
61 | this.addWithTimestamp(entry.getKey(), signed.getPayload()); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | } else { | ||
66 | final Collection<Tuple> tuples = other.get(signature); | ||
67 | for (final Tuple tuple : tuples) { | ||
68 | this.addWithTimestamp(tuple, defaultValue); | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | public boolean isPresentAtInfinityInteral(KeyType key) { | ||
75 | final TimelyMemory<Timestamp> values = this.memoryMap.get(key); | ||
76 | if (values == null) { | ||
77 | return false; | ||
78 | } else { | ||
79 | return values.getCountAtInfinity() != 0; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public void clear() { | ||
85 | this.memoryMap.clear(); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public int getKeysetSize() { | ||
90 | return this.memoryMap.keySet().size(); | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | public int getTotalSize() { | ||
95 | int i = 0; | ||
96 | for (final Entry<KeyType, TimelyMemory<Timestamp>> entry : this.memoryMap.entrySet()) { | ||
97 | i += entry.getValue().size(); | ||
98 | } | ||
99 | return i; | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public Iterator<Tuple> iterator() { | ||
104 | return this.memoryMap.values().stream().flatMap(e -> e.keySet().stream()).iterator(); | ||
105 | } | ||
106 | |||
107 | protected Collection<Tuple> getInternal(final KeyType key) { | ||
108 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(key); | ||
109 | if (memory == null) { | ||
110 | return null; | ||
111 | } else { | ||
112 | return memory.getTuplesAtInfinity(); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | public Map<Tuple, Timeline<Timestamp>> getWithTimestampInternal(final KeyType key) { | ||
117 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(key); | ||
118 | if (memory == null) { | ||
119 | return null; | ||
120 | } else { | ||
121 | return memory.asMap(); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | protected Diff<Timestamp> removeInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { | ||
126 | Timestamp oldResumableTimestamp = null; | ||
127 | Timestamp newResumableTimestamp = null; | ||
128 | |||
129 | final TimelyMemory<Timestamp> keyMemory = this.memoryMap.get(key); | ||
130 | if (keyMemory == null) { | ||
131 | throw raiseDuplicateDeletion(tuple); | ||
132 | } | ||
133 | |||
134 | if (this.isLazy) { | ||
135 | oldResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
136 | } | ||
137 | |||
138 | Diff<Timestamp> diff = null; | ||
139 | try { | ||
140 | diff = keyMemory.remove(tuple, timestamp); | ||
141 | } catch (final IllegalStateException e) { | ||
142 | throw raiseDuplicateDeletion(tuple); | ||
143 | } | ||
144 | if (keyMemory.isEmpty()) { | ||
145 | this.memoryMap.remove(key); | ||
146 | } | ||
147 | |||
148 | if (this.isLazy) { | ||
149 | newResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
150 | if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { | ||
151 | unregisterFoldingState(oldResumableTimestamp, key); | ||
152 | registerFoldingState(newResumableTimestamp, key); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | return diff; | ||
157 | } | ||
158 | |||
159 | protected Diff<Timestamp> addInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { | ||
160 | Timestamp oldResumableTimestamp = null; | ||
161 | Timestamp newResumableTimestamp = null; | ||
162 | |||
163 | final TimelyMemory<Timestamp> keyMemory = this.memoryMap.computeIfAbsent(key, | ||
164 | k -> new TimelyMemory<Timestamp>(this.isLazy)); | ||
165 | |||
166 | if (this.isLazy) { | ||
167 | oldResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
168 | } | ||
169 | |||
170 | final Diff<Timestamp> diff = keyMemory.put(tuple, timestamp); | ||
171 | |||
172 | if (this.isLazy) { | ||
173 | newResumableTimestamp = keyMemory.getResumableTimestamp(); | ||
174 | if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { | ||
175 | unregisterFoldingState(oldResumableTimestamp, key); | ||
176 | registerFoldingState(newResumableTimestamp, key); | ||
177 | } | ||
178 | } | ||
179 | |||
180 | return diff; | ||
181 | } | ||
182 | |||
183 | @Override | ||
184 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
185 | return removeWithTimestamp(tuple, null, timestamp); | ||
186 | } | ||
187 | |||
188 | @Override | ||
189 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
190 | return addWithTimestamp(tuple, null, timestamp); | ||
191 | } | ||
192 | |||
193 | @Override | ||
194 | public boolean isTimely() { | ||
195 | return true; | ||
196 | } | ||
197 | |||
198 | protected void registerFoldingState(final Timestamp timestamp, final KeyType key) { | ||
199 | if (timestamp != null) { | ||
200 | this.foldingStates.compute(timestamp, (k, v) -> { | ||
201 | if (v == null) { | ||
202 | v = CollectionsFactory.createSet(); | ||
203 | } | ||
204 | v.add(key); | ||
205 | return v; | ||
206 | }); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | protected void unregisterFoldingState(final Timestamp timestamp, final KeyType key) { | ||
211 | if (timestamp != null) { | ||
212 | this.foldingStates.compute(timestamp, (k, v) -> { | ||
213 | v.remove(key); | ||
214 | return v.isEmpty() ? null : v; | ||
215 | }); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | @Override | ||
220 | public Timestamp getResumableTimestamp() { | ||
221 | if (this.foldingStates == null || this.foldingStates.isEmpty()) { | ||
222 | return null; | ||
223 | } else { | ||
224 | return this.foldingStates.firstKey(); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java new file mode 100644 index 00000000..ca06685a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java | |||
@@ -0,0 +1,100 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | |||
25 | /** | ||
26 | * Common parts of timely nullary and timely identity implementations. | ||
27 | * | ||
28 | * @noextend This class is not intended to be subclassed by clients. | ||
29 | * @author Tamas Szabo | ||
30 | * @since 2.3 | ||
31 | */ | ||
32 | abstract class AbstractTimelyTrivialMaskedMemory<Timestamp extends Comparable<Timestamp>> extends MaskedTupleMemory<Timestamp> { | ||
33 | |||
34 | protected final TimelyMemory<Timestamp> memory; | ||
35 | |||
36 | protected AbstractTimelyTrivialMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
37 | super(mask, owner); | ||
38 | this.memory = new TimelyMemory<Timestamp>(isLazy); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public void initializeWith(final MaskedTupleMemory<Timestamp> other, final Timestamp defaultValue) { | ||
43 | final Iterable<Tuple> signatures = other.getSignatures(); | ||
44 | for (final Tuple signature : signatures) { | ||
45 | if (other.isTimely()) { | ||
46 | final Map<Tuple, Timeline<Timestamp>> tupleMap = other.getWithTimeline(signature); | ||
47 | for (final Entry<Tuple, Timeline<Timestamp>> entry : tupleMap.entrySet()) { | ||
48 | for (final Signed<Timestamp> signed : entry.getValue().asChangeSequence()) { | ||
49 | if (signed.getDirection() == Direction.DELETE) { | ||
50 | this.removeWithTimestamp(entry.getKey(), signed.getPayload()); | ||
51 | } else { | ||
52 | this.addWithTimestamp(entry.getKey(), signed.getPayload()); | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } else { | ||
57 | final Collection<Tuple> tuples = other.get(signature); | ||
58 | for (final Tuple tuple : tuples) { | ||
59 | this.removeWithTimestamp(tuple, defaultValue); | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public void clear() { | ||
67 | this.memory.clear(); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public int getTotalSize() { | ||
72 | return this.memory.size(); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public Iterator<Tuple> iterator() { | ||
77 | return this.memory.keySet().iterator(); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
82 | return removeWithTimestamp(tuple, null, timestamp); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { | ||
87 | return addWithTimestamp(tuple, null, timestamp); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public boolean isTimely() { | ||
92 | return true; | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public Timestamp getResumableTimestamp() { | ||
97 | return this.memory.getResumableTimestamp(); | ||
98 | } | ||
99 | |||
100 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java new file mode 100644 index 00000000..623d7399 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java | |||
@@ -0,0 +1,98 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.memories.timely; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | |||
25 | /** | ||
26 | * Default timely implementation that covers all cases. | ||
27 | * | ||
28 | * @author Tamas Szabo | ||
29 | * @since 2.3 | ||
30 | */ | ||
31 | public final class TimelyDefaultMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
32 | extends AbstractTimelyMaskedMemory<Timestamp, Tuple> { | ||
33 | |||
34 | public TimelyDefaultMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
35 | super(mask, owner, isLazy); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Iterable<Tuple> getSignatures() { | ||
40 | return this.memoryMap.keySet(); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, | ||
45 | final Timestamp timestamp) { | ||
46 | final Tuple key = mask.transform(tuple); | ||
47 | return removeInternal(key, tuple, timestamp); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, | ||
52 | final Timestamp timestamp) { | ||
53 | final Tuple key = this.mask.transform(tuple); | ||
54 | return addInternal(key, tuple, timestamp); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public Collection<Tuple> get(final ITuple signature) { | ||
59 | return getInternal(signature.toImmutable()); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
64 | return getWithTimestampInternal(signature.toImmutable()); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
69 | return isPresentAtInfinityInteral(signature.toImmutable()); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public Set<Tuple> getResumableSignatures() { | ||
74 | if (this.foldingStates == null || this.foldingStates.isEmpty()) { | ||
75 | return Collections.emptySet(); | ||
76 | } else { | ||
77 | return this.foldingStates.firstEntry().getValue(); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
83 | final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap(); | ||
84 | final Timestamp resumableTimestamp = this.getResumableTimestamp(); | ||
85 | if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) { | ||
86 | throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); | ||
87 | } | ||
88 | final Set<Tuple> signatures = this.foldingStates.remove(timestamp); | ||
89 | for (final Tuple signature : signatures) { | ||
90 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(signature); | ||
91 | final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(resumableTimestamp); | ||
92 | result.put(signature, diffMap); | ||
93 | registerFoldingState(memory.getResumableTimestamp(), signature); | ||
94 | } | ||
95 | return result; | ||
96 | } | ||
97 | |||
98 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java new file mode 100644 index 00000000..568f274d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java | |||
@@ -0,0 +1,106 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
23 | |||
24 | /** | ||
25 | * Timely specialization for identity mask. | ||
26 | * | ||
27 | * @author Tamas Szabo | ||
28 | * @since 2.3 | ||
29 | */ | ||
30 | public final class TimelyIdentityMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
31 | extends AbstractTimelyTrivialMaskedMemory<Timestamp> { | ||
32 | |||
33 | public TimelyIdentityMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
34 | super(mask, owner, isLazy); | ||
35 | if (!mask.isIdentity()) | ||
36 | throw new IllegalArgumentException(mask.toString()); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public int getKeysetSize() { | ||
41 | return this.memory.size(); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Iterable<Tuple> getSignatures() { | ||
46 | return this.memory.keySet(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public Collection<Tuple> get(final ITuple signature) { | ||
51 | if (this.memory.getTuplesAtInfinity().contains(signature)) { | ||
52 | return Collections.singleton(signature.toImmutable()); | ||
53 | } else { | ||
54 | return null; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
60 | final Timeline<Timestamp> value = this.memory.get(signature); | ||
61 | if (value != null) { | ||
62 | return Collections.singletonMap(signature.toImmutable(), value); | ||
63 | } else { | ||
64 | return null; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
70 | try { | ||
71 | return this.memory.remove(tuple, timestamp); | ||
72 | } catch (final IllegalStateException e) { | ||
73 | throw raiseDuplicateDeletion(tuple); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
79 | return this.memory.put(tuple, timestamp); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
84 | return this.memory.isPresentAtInfinity(signature.toImmutable()); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public Set<Tuple> getResumableSignatures() { | ||
89 | if (this.memory.getResumableTimestamp() != null) { | ||
90 | return this.memory.getResumableTuples(); | ||
91 | } else { | ||
92 | return Collections.emptySet(); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
98 | final Map<Tuple, Diff<Timestamp>> diffMap = this.memory.resumeAt(timestamp); | ||
99 | final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap(); | ||
100 | for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) { | ||
101 | result.put(entry.getKey(), Collections.singletonMap(entry.getKey(), entry.getValue())); | ||
102 | } | ||
103 | return result; | ||
104 | } | ||
105 | |||
106 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java new file mode 100644 index 00000000..75987a89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
22 | |||
23 | /** | ||
24 | * Timely specialization for nullary mask. | ||
25 | * | ||
26 | * @author Tamas Szabo | ||
27 | * @since 2.3 | ||
28 | */ | ||
29 | public final class TimelyNullaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
30 | extends AbstractTimelyTrivialMaskedMemory<Timestamp> { | ||
31 | |||
32 | protected static final Tuple EMPTY_TUPLE = Tuples.staticArityFlatTupleOf(); | ||
33 | protected static final Set<Tuple> UNIT_RELATION = Collections.singleton(EMPTY_TUPLE); | ||
34 | protected static final Set<Tuple> EMPTY_RELATION = Collections.emptySet(); | ||
35 | |||
36 | public TimelyNullaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
37 | super(mask, owner, isLazy); | ||
38 | if (0 != mask.getSize()) { | ||
39 | throw new IllegalArgumentException(mask.toString()); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public int getKeysetSize() { | ||
45 | return this.memory.isEmpty() ? 0 : 1; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Iterable<Tuple> getSignatures() { | ||
50 | return this.memory.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Collection<Tuple> get(final ITuple signature) { | ||
55 | if (0 == signature.getSize()) { | ||
56 | return this.memory.getTuplesAtInfinity(); | ||
57 | } else { | ||
58 | return null; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
64 | if (0 == signature.getSize()) { | ||
65 | return this.memory.asMap(); | ||
66 | } else { | ||
67 | return null; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
73 | try { | ||
74 | return this.memory.remove(tuple, timestamp); | ||
75 | } catch (final IllegalStateException e) { | ||
76 | throw raiseDuplicateDeletion(tuple); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
82 | return this.memory.put(tuple, timestamp); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public boolean isPresentAtInfinity(final ITuple signature) { | ||
87 | if (0 == signature.getSize()) { | ||
88 | return this.memory.getCountAtInfinity() > 0; | ||
89 | } else { | ||
90 | return false; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public Set<Tuple> getResumableSignatures() { | ||
96 | if (this.memory.getResumableTimestamp() != null) { | ||
97 | return UNIT_RELATION; | ||
98 | } else { | ||
99 | return EMPTY_RELATION; | ||
100 | } | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
105 | return Collections.singletonMap(EMPTY_TUPLE, this.memory.resumeAt(timestamp)); | ||
106 | } | ||
107 | |||
108 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java new file mode 100644 index 00000000..178193af --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java | |||
@@ -0,0 +1,133 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.memories.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
25 | |||
26 | /** | ||
27 | * Timely specialization for unary mask. | ||
28 | * | ||
29 | * @author Tamas Szabo | ||
30 | * @since 2.3 | ||
31 | */ | ||
32 | public final class TimelyUnaryMaskedTupleMemory<Timestamp extends Comparable<Timestamp>> | ||
33 | extends AbstractTimelyMaskedMemory<Timestamp, Object> { | ||
34 | |||
35 | protected final int keyPosition; | ||
36 | |||
37 | public TimelyUnaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { | ||
38 | super(mask, owner, isLazy); | ||
39 | if (1 != mask.getSize()) | ||
40 | throw new IllegalArgumentException(mask.toString()); | ||
41 | this.keyPosition = mask.indices[0]; | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Iterable<Tuple> getSignatures() { | ||
46 | return () -> { | ||
47 | final Iterator<Object> wrapped = this.memoryMap.keySet().iterator(); | ||
48 | return new Iterator<Tuple>() { | ||
49 | @Override | ||
50 | public boolean hasNext() { | ||
51 | return wrapped.hasNext(); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public Tuple next() { | ||
56 | final Object key = wrapped.next(); | ||
57 | return Tuples.staticArityFlatTupleOf(key); | ||
58 | } | ||
59 | }; | ||
60 | }; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Diff<Timestamp> removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
65 | final Object key = tuple.get(keyPosition); | ||
66 | return removeInternal(key, tuple, timestamp); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public Diff<Timestamp> addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { | ||
71 | final Object key = tuple.get(keyPosition); | ||
72 | return addInternal(key, tuple, timestamp); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public Collection<Tuple> get(final ITuple signature) { | ||
77 | return getInternal(signature.get(0)); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Map<Tuple, Timeline<Timestamp>> getWithTimeline(final ITuple signature) { | ||
82 | return getWithTimestampInternal(signature.get(0)); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public boolean isPresentAtInfinity(ITuple signature) { | ||
87 | return isPresentAtInfinityInteral(signature.get(0)); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public Iterable<Tuple> getResumableSignatures() { | ||
92 | if (this.foldingStates == null || this.foldingStates.isEmpty()) { | ||
93 | return Collections.emptySet(); | ||
94 | } else { | ||
95 | return () -> { | ||
96 | final Iterator<Object> wrapped = this.foldingStates.firstEntry().getValue().iterator(); | ||
97 | return new Iterator<Tuple>() { | ||
98 | @Override | ||
99 | public boolean hasNext() { | ||
100 | return wrapped.hasNext(); | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Tuple next() { | ||
105 | final Object key = wrapped.next(); | ||
106 | return Tuples.staticArityFlatTupleOf(key); | ||
107 | } | ||
108 | }; | ||
109 | }; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp) { | ||
115 | final Map<Tuple, Map<Tuple, Diff<Timestamp>>> result = CollectionsFactory.createMap(); | ||
116 | final Timestamp resumableTimestamp = this.getResumableTimestamp(); | ||
117 | if (resumableTimestamp == null) { | ||
118 | throw new IllegalStateException("There is nothing to fold!"); | ||
119 | } else if (resumableTimestamp.compareTo(timestamp) != 0) { | ||
120 | throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); | ||
121 | } | ||
122 | |||
123 | final Set<Object> signatures = this.foldingStates.remove(timestamp); | ||
124 | for (final Object signature : signatures) { | ||
125 | final TimelyMemory<Timestamp> memory = this.memoryMap.get(signature); | ||
126 | final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(resumableTimestamp); | ||
127 | result.put(Tuples.staticArityFlatTupleOf(signature), diffMap); | ||
128 | registerFoldingState(memory.getResumableTimestamp(), signature); | ||
129 | } | ||
130 | return result; | ||
131 | } | ||
132 | |||
133 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java new file mode 100644 index 00000000..c9f4b305 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning; | ||
11 | |||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
19 | |||
20 | /** | ||
21 | * | ||
22 | * An implicit common parameter is the "effort" PatternDescription. This | ||
23 | * indicates that the build request is part of an effort to build the matcher of | ||
24 | * the given pattern; it it important to record this during code generation so | ||
25 | * that the generated code can be separated according to patterns. | ||
26 | * | ||
27 | * @param <Collector> | ||
28 | * the handle of a receiver-like RETE ending to which plans can be | ||
29 | * connected | ||
30 | * @author Gabor Bergmann | ||
31 | * @noimplement This interface is not intended to be implemented by clients. | ||
32 | */ | ||
33 | public interface IOperationCompiler<Collector> { | ||
34 | |||
35 | /** | ||
36 | * @throws ViatraQueryRuntimeException | ||
37 | */ | ||
38 | public Collector patternCollector(PQuery pattern); | ||
39 | |||
40 | public void buildConnection(SubPlan parentPlan, Collector collector); | ||
41 | |||
42 | /** | ||
43 | * @since 0.9 | ||
44 | */ | ||
45 | public void patternFinished(PQuery pattern, Collector collector); | ||
46 | |||
47 | /** | ||
48 | * @throws ViatraQueryRuntimeException | ||
49 | */ | ||
50 | public SubPlan patternCallPlan(Tuple nodes, PQuery supplierKey); | ||
51 | |||
52 | public SubPlan transitiveInstantiationPlan(Tuple nodes); | ||
53 | |||
54 | public SubPlan directInstantiationPlan(Tuple nodes); | ||
55 | |||
56 | public SubPlan transitiveGeneralizationPlan(Tuple nodes); | ||
57 | |||
58 | public SubPlan directGeneralizationPlan(Tuple nodes); | ||
59 | |||
60 | public SubPlan transitiveContainmentPlan(Tuple nodes); | ||
61 | |||
62 | public SubPlan directContainmentPlan(Tuple nodes); | ||
63 | |||
64 | public SubPlan binaryEdgeTypePlan(Tuple nodes, Object supplierKey); | ||
65 | |||
66 | public SubPlan ternaryEdgeTypePlan(Tuple nodes, Object supplierKey); | ||
67 | |||
68 | public SubPlan unaryTypePlan(Tuple nodes, Object supplierKey); | ||
69 | |||
70 | public SubPlan buildStartingPlan(Object[] constantValues, Object[] constantNames); | ||
71 | |||
72 | public SubPlan buildEqualityChecker(SubPlan parentPlan, int[] indices); | ||
73 | |||
74 | public SubPlan buildInjectivityChecker(SubPlan parentPlan, int subject, int[] inequalIndices); | ||
75 | |||
76 | public SubPlan buildTransitiveClosure(SubPlan parentPlan); | ||
77 | |||
78 | public SubPlan buildTrimmer(SubPlan parentPlan, TupleMask trimMask, boolean enforceUniqueness); | ||
79 | |||
80 | public SubPlan buildBetaNode(SubPlan primaryPlan, SubPlan sidePlan, | ||
81 | TupleMask primaryMask, TupleMask sideMask, TupleMask complementer, boolean negative); | ||
82 | |||
83 | public SubPlan buildCounterBetaNode(SubPlan primaryPlan, SubPlan sidePlan, | ||
84 | TupleMask primaryMask, TupleMask originalSideMask, TupleMask complementer, | ||
85 | Object aggregateResultCalibrationElement); | ||
86 | |||
87 | public SubPlan buildCountCheckBetaNode(SubPlan primaryPlan, SubPlan sidePlan, | ||
88 | TupleMask primaryMask, TupleMask originalSideMask, int resultPositionInSignature); | ||
89 | |||
90 | public SubPlan buildPredicateChecker(IExpressionEvaluator evaluator, Map<String, Integer> tupleNameMap, | ||
91 | SubPlan parentPlan); | ||
92 | public SubPlan buildFunctionEvaluator(IExpressionEvaluator evaluator, Map<String, Integer> tupleNameMap, | ||
93 | SubPlan parentPlan, Object computedResultCalibrationElement); | ||
94 | |||
95 | /** | ||
96 | * @return an operation compiler that potentially acts on a separate container | ||
97 | */ | ||
98 | public IOperationCompiler<Collector> getNextContainer(); | ||
99 | |||
100 | /** | ||
101 | * @return an operation compiler that puts build actions on the tab of the given pattern | ||
102 | * @since 0.9 | ||
103 | */ | ||
104 | public IOperationCompiler<Collector> putOnTab(PQuery effort /*, IPatternMatcherContext context*/); | ||
105 | |||
106 | public void reinitialize(); | ||
107 | |||
108 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java new file mode 100644 index 00000000..6ce9d91b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning; | ||
11 | |||
12 | import org.apache.log4j.Logger; | ||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
16 | |||
17 | /** | ||
18 | * An algorithm that builds a query plan based on a PSystem representation of a body of constraints. This interface is | ||
19 | * for internal use of the various query backends. | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | */ | ||
23 | public interface IQueryPlannerStrategy { | ||
24 | |||
25 | /** | ||
26 | * @throws ViatraQueryRuntimeException | ||
27 | */ | ||
28 | public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context); | ||
29 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java new file mode 100644 index 00000000..501ddf73 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java | |||
@@ -0,0 +1,102 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.planning; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
12 | |||
13 | /** | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * @since 0.9 | ||
16 | */ | ||
17 | public class QueryProcessingException extends ViatraQueryRuntimeException { | ||
18 | |||
19 | private static final long serialVersionUID = -8272290113656867086L; | ||
20 | /** | ||
21 | * Binding the '{n}' (n = 1..N) strings to contextual conditions in 'context' | ||
22 | * | ||
23 | * @param context | ||
24 | * : array of context-sensitive Strings | ||
25 | */ | ||
26 | protected static String bind(String message, String[] context) { | ||
27 | if (context == null) | ||
28 | return message; | ||
29 | |||
30 | String internal = message; | ||
31 | for (int i = 0; i < context.length; i++) { | ||
32 | internal = internal.replace("{" + (i + 1) + "}", context[i] != null ? context[i] : "<<null>>"); | ||
33 | } | ||
34 | return internal; | ||
35 | } | ||
36 | |||
37 | private Object patternDescription; | ||
38 | private String shortMessage; | ||
39 | |||
40 | /** | ||
41 | * @param message | ||
42 | * The template of the exception message | ||
43 | * @param context | ||
44 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
45 | * defined | ||
46 | * @param patternDescription | ||
47 | * the PatternDescription where the exception occurred | ||
48 | * @since 2.0 | ||
49 | */ | ||
50 | public QueryProcessingException(String message, Object patternDescription) { | ||
51 | super(message); | ||
52 | initializeFields(message, patternDescription); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * @param message | ||
57 | * The template of the exception message | ||
58 | * @param context | ||
59 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
60 | * defined | ||
61 | * @param patternDescription | ||
62 | * the PatternDescription where the exception occurred | ||
63 | */ | ||
64 | public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription) { | ||
65 | super(bind(message, context)); | ||
66 | initializeFields(shortMessage, patternDescription); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * @param message | ||
71 | * The template of the exception message | ||
72 | * @param context | ||
73 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
74 | * defined | ||
75 | * @param patternDescription | ||
76 | * the PatternDescription where the exception occurred | ||
77 | */ | ||
78 | public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription, | ||
79 | Throwable cause) { | ||
80 | super(bind(message, context), cause); | ||
81 | initializeFields(shortMessage, patternDescription); | ||
82 | } | ||
83 | |||
84 | public Object getPatternDescription() { | ||
85 | return patternDescription; | ||
86 | } | ||
87 | |||
88 | public String getShortMessage() { | ||
89 | return shortMessage; | ||
90 | } | ||
91 | |||
92 | private void initializeFields(String shortMessage, Object patternDescription) { | ||
93 | this.patternDescription = patternDescription; | ||
94 | this.shortMessage = shortMessage; | ||
95 | } | ||
96 | |||
97 | |||
98 | public void setPatternDescription(Object patternDescription) { | ||
99 | this.patternDescription = patternDescription; | ||
100 | } | ||
101 | |||
102 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java new file mode 100644 index 00000000..1998df9d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java | |||
@@ -0,0 +1,240 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Set; | ||
16 | import java.util.WeakHashMap; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
28 | |||
29 | /** | ||
30 | * A plan representing a subset of (or possibly all the) constraints evaluated. A SubPlan instance is responsible for | ||
31 | * representing a state of the plan; but after it is initialized it is expected be immutable | ||
32 | * (exception: inferred constraints, see {@link #inferConstraint(PConstraint)}). | ||
33 | * | ||
34 | * <p> A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. | ||
35 | * Important maintained information: <ul> | ||
36 | * <li>set of <b>variables</b> whose values are known when the runtime evaluation is at this stage, | ||
37 | * <li>set of <b>constraints</b> that are known to hold true at this point. | ||
38 | * </ul> | ||
39 | * | ||
40 | * <p> Recommended to instantiate via a {@link SubPlanFactory} or subclasses, | ||
41 | * so that query planners can subclass SubPlan if needed. | ||
42 | * | ||
43 | * @author Gabor Bergmann | ||
44 | * | ||
45 | */ | ||
46 | public class SubPlan { | ||
47 | private PBody body; | ||
48 | private List<? extends SubPlan> parentPlans; | ||
49 | private POperation operation; | ||
50 | |||
51 | private final Set<PVariable> visibleVariables; | ||
52 | private final Set<PVariable> allVariables; | ||
53 | private final Set<PVariable> introducedVariables; // delta compared to first parent | ||
54 | private Set<PConstraint> allConstraints; | ||
55 | private Set<PConstraint> deltaConstraints; // delta compared to all parents | ||
56 | private Set<PConstraint> externallyInferredConstraints; // inferred in addition to direct consequences of the operation and parents | ||
57 | |||
58 | |||
59 | |||
60 | |||
61 | |||
62 | /** | ||
63 | * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. | ||
64 | */ | ||
65 | public SubPlan(PBody body, POperation operation, SubPlan... parentPlans) { | ||
66 | this(body, operation, Arrays.asList(parentPlans)); | ||
67 | } | ||
68 | /** | ||
69 | * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. | ||
70 | */ | ||
71 | public SubPlan(PBody body, POperation operation, List<? extends SubPlan> parentPlans) { | ||
72 | super(); | ||
73 | this.body = body; | ||
74 | this.parentPlans = parentPlans; | ||
75 | this.operation = operation; | ||
76 | |||
77 | this.externallyInferredConstraints = new HashSet<PConstraint>(); | ||
78 | this.deltaConstraints = new HashSet<PConstraint>(operation.getDeltaConstraints()); | ||
79 | |||
80 | this.allVariables = new HashSet<PVariable>(); | ||
81 | for (PConstraint constraint: deltaConstraints) { | ||
82 | this.allVariables.addAll(constraint.getDeducedVariables()); | ||
83 | } | ||
84 | this.allConstraints = new HashSet<PConstraint>(deltaConstraints); | ||
85 | for (SubPlan parentPlan: parentPlans) { | ||
86 | this.allConstraints.addAll(parentPlan.getAllEnforcedConstraints()); | ||
87 | this.allVariables.addAll(parentPlan.getAllDeducedVariables()); | ||
88 | } | ||
89 | |||
90 | // TODO this is ugly a bit | ||
91 | if (operation instanceof PStart) { | ||
92 | this.visibleVariables = new HashSet<PVariable>(((PStart) operation).getAPrioriVariables()); | ||
93 | this.allVariables.addAll(visibleVariables); | ||
94 | } else if (operation instanceof PProject) { | ||
95 | this.visibleVariables = new HashSet<PVariable>(((PProject) operation).getToVariables()); | ||
96 | } else { | ||
97 | this.visibleVariables = new HashSet<PVariable>(); | ||
98 | for (SubPlan parentPlan: parentPlans) | ||
99 | this.visibleVariables.addAll(parentPlan.getVisibleVariables()); | ||
100 | for (PConstraint constraint: deltaConstraints) | ||
101 | this.visibleVariables.addAll(constraint.getDeducedVariables()); | ||
102 | } | ||
103 | |||
104 | this.introducedVariables = new HashSet<PVariable>(this.visibleVariables); | ||
105 | if (!parentPlans.isEmpty()) | ||
106 | introducedVariables.removeAll(parentPlans.get(0).getVisibleVariables()); | ||
107 | |||
108 | operation.checkConsistency(this); | ||
109 | } | ||
110 | |||
111 | |||
112 | @Override | ||
113 | public String toString() { | ||
114 | return toLongString(); | ||
115 | } | ||
116 | public String toShortString() { | ||
117 | return String.format("Plan{%s}:%s", | ||
118 | visibleVariables.stream().map(PVariable::getName).collect(Collectors.joining(",")), | ||
119 | operation.getShortName()); | ||
120 | } | ||
121 | public String toLongString() { | ||
122 | return String.format("%s<%s>", | ||
123 | toShortString(), | ||
124 | parentPlans.stream().map(Object::toString).collect(Collectors.joining("; "))); | ||
125 | } | ||
126 | |||
127 | |||
128 | /** | ||
129 | * All constraints that are known to hold at this point | ||
130 | */ | ||
131 | public Set<PConstraint> getAllEnforcedConstraints() { | ||
132 | return allConstraints; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * The new constraints enforced at this stage of plan, that aren't yet enforced at parents | ||
137 | * (results are also included in {@link SubPlan#getAllEnforcedConstraints()}) | ||
138 | */ | ||
139 | public Set<PConstraint> getDeltaEnforcedConstraints() { | ||
140 | return deltaConstraints; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Indicate that a given constraint was found to be automatically satisfied at this point | ||
145 | * without additional operations. | ||
146 | * (results are also included in {@link SubPlan#getDeltaEnforcedConstraints()}) | ||
147 | * | ||
148 | * <p>Warning: not propagated automatically to child plans, | ||
149 | * so best to invoke before constructing further SubPlans. </p> | ||
150 | */ | ||
151 | public void inferConstraint(PConstraint constraint) { | ||
152 | externallyInferredConstraints.add(constraint); | ||
153 | deltaConstraints.add(constraint); | ||
154 | allConstraints.add(constraint); | ||
155 | } | ||
156 | |||
157 | public PBody getBody() { | ||
158 | return body; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * Variables which are assigned a value at this point | ||
163 | * (results are also included in {@link SubPlan#getAllDeducedVariables()}) | ||
164 | */ | ||
165 | public Set<PVariable> getVisibleVariables() { | ||
166 | return visibleVariables; | ||
167 | } | ||
168 | /** | ||
169 | * Variables which have been assigned a value; | ||
170 | * includes visible variables (see {@link #getVisibleVariables()}) | ||
171 | * and additionally any variables hidden by a projection (see {@link PProject}). | ||
172 | */ | ||
173 | public Set<PVariable> getAllDeducedVariables() { | ||
174 | return allVariables; | ||
175 | } | ||
176 | /** | ||
177 | * Delta compared to first parent: variables that are visible here but were not visible at the first parent. | ||
178 | */ | ||
179 | public Set<PVariable> getIntroducedVariables() { | ||
180 | return introducedVariables; | ||
181 | } | ||
182 | public List<? extends SubPlan> getParentPlans() { | ||
183 | return parentPlans; | ||
184 | } | ||
185 | public POperation getOperation() { | ||
186 | return operation; | ||
187 | } | ||
188 | |||
189 | |||
190 | /** | ||
191 | * The closure of all type judgments of enforced constraints at this point. | ||
192 | * <p> No subsumption applied. | ||
193 | */ | ||
194 | public Set<TypeJudgement> getAllImpliedTypeJudgements(IQueryMetaContext context) { | ||
195 | Set<TypeJudgement> impliedJudgements = allImpliedTypeJudgements.get(context); | ||
196 | if (impliedJudgements == null) { | ||
197 | Set<TypeJudgement> equivalentJudgements = TypeHelper.getDirectJudgements(getAllEnforcedConstraints(), context); | ||
198 | impliedJudgements = TypeHelper.typeClosure(equivalentJudgements, context); | ||
199 | |||
200 | allImpliedTypeJudgements.put(context, impliedJudgements); | ||
201 | } | ||
202 | return impliedJudgements; | ||
203 | } | ||
204 | private WeakHashMap<IQueryMetaContext, Set<TypeJudgement>> allImpliedTypeJudgements = new WeakHashMap<IQueryMetaContext, Set<TypeJudgement>>(); | ||
205 | |||
206 | |||
207 | @Override | ||
208 | public int hashCode() { | ||
209 | final int prime = 31; | ||
210 | int result = 1; | ||
211 | result = prime * result | ||
212 | + ((operation == null) ? 0 : operation.hashCode()); | ||
213 | result = prime * result | ||
214 | + ((parentPlans == null) ? 0 : parentPlans.hashCode()); | ||
215 | return result; | ||
216 | } | ||
217 | @Override | ||
218 | public boolean equals(Object obj) { | ||
219 | if (this == obj) | ||
220 | return true; | ||
221 | if (obj == null) | ||
222 | return false; | ||
223 | if (!(obj instanceof SubPlan)) | ||
224 | return false; | ||
225 | SubPlan other = (SubPlan) obj; | ||
226 | if (operation == null) { | ||
227 | if (other.operation != null) | ||
228 | return false; | ||
229 | } else if (!operation.equals(other.operation)) | ||
230 | return false; | ||
231 | if (parentPlans == null) { | ||
232 | if (other.parentPlans != null) | ||
233 | return false; | ||
234 | } else if (!parentPlans.equals(other.parentPlans)) | ||
235 | return false; | ||
236 | return true; | ||
237 | } | ||
238 | |||
239 | |||
240 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java new file mode 100644 index 00000000..d0df5fac --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.planning; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
13 | |||
14 | /** | ||
15 | * Single entry point for creating subplans. | ||
16 | * Can be subclassed by query planner to provide specialized SubPlans. | ||
17 | * @author Bergmann Gabor | ||
18 | * | ||
19 | */ | ||
20 | public class SubPlanFactory { | ||
21 | |||
22 | protected PBody body; | ||
23 | |||
24 | public SubPlanFactory(PBody body) { | ||
25 | super(); | ||
26 | this.body = body; | ||
27 | } | ||
28 | |||
29 | public SubPlan createSubPlan(POperation operation, SubPlan... parentPlans) { | ||
30 | return new SubPlan(body, operation, parentPlans); | ||
31 | } | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java new file mode 100644 index 00000000..ed5d1cbb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java | |||
@@ -0,0 +1,165 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning.helpers; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
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.PVariable; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * | ||
32 | */ | ||
33 | public class BuildHelper { | ||
34 | |||
35 | private BuildHelper() { | ||
36 | // Hiding constructor for utility class | ||
37 | } | ||
38 | |||
39 | // public static SubPlan naturalJoin(IOperationCompiler buildable, | ||
40 | // SubPlan primaryPlan, SubPlan secondaryPlan) { | ||
41 | // JoinHelper joinHelper = new JoinHelper(primaryPlan, secondaryPlan); | ||
42 | // return buildable.buildBetaNode(primaryPlan, secondaryPlan, joinHelper.getPrimaryMask(), | ||
43 | // joinHelper.getSecondaryMask(), joinHelper.getComplementerMask(), false); | ||
44 | // } | ||
45 | |||
46 | |||
47 | /** | ||
48 | * Reduces the number of tuples by trimming (existentially quantifying) the set of variables that <ul> | ||
49 | * <li> are visible in the subplan, | ||
50 | * <li> are not exported parameters, | ||
51 | * <li> have all their constraints already enforced in the subplan, | ||
52 | * </ul> and thus will not be needed anymore. | ||
53 | * | ||
54 | * @param onlyIfNotDetermined if true, no trimming performed unless there is at least one variable that is not functionally determined | ||
55 | * @return the plan after the trimming (possibly the original) | ||
56 | * @since 1.5 | ||
57 | */ | ||
58 | public static SubPlan trimUnneccessaryVariables(SubPlanFactory planFactory, /*IOperationCompiler buildable,*/ | ||
59 | SubPlan plan, boolean onlyIfNotDetermined, QueryAnalyzer analyzer) { | ||
60 | Set<PVariable> canBeTrimmed = new HashSet<PVariable>(); | ||
61 | Set<PVariable> variablesInPlan = plan.getVisibleVariables(); | ||
62 | for (PVariable trimCandidate : variablesInPlan) { | ||
63 | if (trimCandidate.getReferringConstraintsOfType(ExportedParameter.class).isEmpty()) { | ||
64 | if (plan.getAllEnforcedConstraints().containsAll(trimCandidate.getReferringConstraints())) | ||
65 | canBeTrimmed.add(trimCandidate); | ||
66 | } | ||
67 | } | ||
68 | final Set<PVariable> retainedVars = setMinus(variablesInPlan, canBeTrimmed); | ||
69 | if (!canBeTrimmed.isEmpty() && !(onlyIfNotDetermined && areVariablesDetermined(plan, retainedVars, canBeTrimmed, analyzer, false))) { | ||
70 | // TODO add smart ordering? | ||
71 | plan = planFactory.createSubPlan(new PProject(retainedVars), plan); | ||
72 | } | ||
73 | return plan; | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * @return true iff a set of given variables functionally determine all visible variables in the subplan according to the subplan's constraints | ||
78 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
79 | * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
80 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
81 | * @since 1.5 | ||
82 | */ | ||
83 | public static boolean areAllVariablesDetermined(SubPlan plan, Collection<PVariable> determining, QueryAnalyzer analyzer, boolean strict) { | ||
84 | return areVariablesDetermined(plan, determining, plan.getVisibleVariables(), analyzer, strict); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * @return true iff one set of given variables functionally determine the other set according to the subplan's constraints | ||
89 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
90 | * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
91 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
92 | * @since 1.5 | ||
93 | */ | ||
94 | public static boolean areVariablesDetermined(SubPlan plan, Collection<PVariable> determining, Collection<PVariable> determined, | ||
95 | QueryAnalyzer analyzer, boolean strict) { | ||
96 | Map<Set<PVariable>, Set<PVariable>> dependencies = analyzer.getFunctionalDependencies(plan.getAllEnforcedConstraints(), strict); | ||
97 | final Set<PVariable> closure = FunctionalDependencyHelper.closureOf(determining, dependencies); | ||
98 | final boolean isDetermined = closure.containsAll(determined); | ||
99 | return isDetermined; | ||
100 | } | ||
101 | |||
102 | private static <T> Set<T> setMinus(Set<T> a, Set<T> b) { | ||
103 | Set<T> difference = new HashSet<T>(a); | ||
104 | difference.removeAll(b); | ||
105 | return difference; | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Finds an arbitrary constraint that is not enforced at the given plan. | ||
110 | * | ||
111 | * @param pSystem | ||
112 | * @param plan | ||
113 | * @return a PConstraint that is not enforced, if any, or null if all are enforced | ||
114 | */ | ||
115 | public static PConstraint getAnyUnenforcedConstraint(PBody pSystem, | ||
116 | SubPlan plan) { | ||
117 | Set<PConstraint> allEnforcedConstraints = plan.getAllEnforcedConstraints(); | ||
118 | Set<PConstraint> constraints = pSystem.getConstraints(); | ||
119 | for (PConstraint pConstraint : constraints) { | ||
120 | if (!allEnforcedConstraints.contains(pConstraint)) | ||
121 | return pConstraint; | ||
122 | } | ||
123 | return null; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Skips the last few steps, if any, that are projections, so that a custom projection can be added instead. | ||
128 | * Useful for connecting body final plans into the production node. | ||
129 | * | ||
130 | * @since 2.1 | ||
131 | */ | ||
132 | public static SubPlan eliminateTrailingProjections(SubPlan plan) { | ||
133 | while (plan.getOperation() instanceof PProject) | ||
134 | plan = plan.getParentPlans().get(0); | ||
135 | return plan; | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Verifies whether all constraints are enforced and exported parameters are present. | ||
140 | * | ||
141 | * @param pSystem | ||
142 | * @param plan | ||
143 | * @throws ViatraQueryRuntimeException | ||
144 | */ | ||
145 | public static void finalCheck(final PBody pSystem, SubPlan plan, IQueryMetaContext context) { | ||
146 | PConstraint unenforcedConstraint = getAnyUnenforcedConstraint(pSystem, plan); | ||
147 | if (unenforcedConstraint != null) { | ||
148 | throw new QueryProcessingException( | ||
149 | "Pattern matcher construction terminated without successfully enforcing constraint {1}." | ||
150 | + " Could be caused if the value of some variables can not be deduced, e.g. by circularity of pattern constraints.", | ||
151 | new String[] { unenforcedConstraint.toString() }, "Could not enforce a pattern constraint", null); | ||
152 | } | ||
153 | for (ExportedParameter export : pSystem | ||
154 | .getConstraintsOfType(ExportedParameter.class)) { | ||
155 | if (!export.isReadyAt(plan, context)) { | ||
156 | throw new QueryProcessingException( | ||
157 | "Exported pattern parameter {1} could not be deduced during pattern matcher construction." | ||
158 | + " A pattern constraint is required to positively deduce its value.", | ||
159 | new String[] { export.getParameterName() }, "Could not calculate pattern parameter", | ||
160 | null); | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java new file mode 100644 index 00000000..40835f52 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java | |||
@@ -0,0 +1,143 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Adam Dudas, 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.planning.helpers; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Map; | ||
16 | import java.util.Map.Entry; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.util.Sets; | ||
20 | |||
21 | /** | ||
22 | * Helper utility class for functional dependency analysis. | ||
23 | * | ||
24 | * Throughout this class attribute sets are represented as generic sets and functional dependencies as maps from | ||
25 | * attribute set (generic sets) to attribute set (generic sets) | ||
26 | * | ||
27 | * @author Adam Dudas | ||
28 | * | ||
29 | */ | ||
30 | public class FunctionalDependencyHelper { | ||
31 | |||
32 | private FunctionalDependencyHelper() { | ||
33 | // Hiding constructor for utility class | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Get the closure of the specified attribute set relative to the specified functional dependencies. | ||
38 | * | ||
39 | * @param attributes | ||
40 | * The attributes to get the closure of. | ||
41 | * @param dependencies | ||
42 | * The functional dependencies of which the closure operation is relative to. | ||
43 | * @return The closure of the specified attribute set relative to the specified functional dependencies. | ||
44 | */ | ||
45 | public static <A> Set<A> closureOf(Collection<A> attributes, Map<Set<A>, Set<A>> dependencies) { | ||
46 | Set<A> closureSet = new HashSet<A>(); | ||
47 | |||
48 | for (Set<A> closureSet1 = new HashSet<A>(attributes); closureSet.addAll(closureSet1);) { | ||
49 | closureSet1 = new HashSet<A>(); | ||
50 | for (Entry<Set<A>, Set<A>> dependency : dependencies.entrySet()) { | ||
51 | if (closureSet.containsAll(dependency.getKey())) | ||
52 | closureSet1.addAll(dependency.getValue()); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | return closureSet; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * @return true if the dependency from the left set to the right set is trivial | ||
61 | * @since 1.5 | ||
62 | */ | ||
63 | public static <A> boolean isTrivial(Set<A> left, Set<A> right) { | ||
64 | return left.containsAll(right); | ||
65 | } | ||
66 | |||
67 | /*** | ||
68 | * Returns the dependency set over attributes in {@link targetAttributes} that are implied by a given source dependency set. | ||
69 | * <p> Note: exponential in the size of the target attribute set. | ||
70 | * <p> Note: minimality of the returned dependency set is currently not guaranteed. | ||
71 | * @param originalDependencies all dependencies that are known to hold on a wider set of attributes | ||
72 | * @param targetAttributes the set of attributes we are interested in | ||
73 | * @since 1.5 | ||
74 | */ | ||
75 | public static <A> Map<Set<A>, Set<A>> projectDependencies(Map<Set<A>, Set<A>> originalDependencies, Set<A> targetAttributes) { | ||
76 | // only those attributes are considered as left-hand-side candidates that occur at least once in dependencies | ||
77 | Set<A> leftCandidates = new HashSet<A>(); | ||
78 | for (Entry<Set<A>, Set<A>> dependency : originalDependencies.entrySet()) { | ||
79 | if (!isTrivial(dependency.getKey(), dependency.getValue())) // only if non-trivial | ||
80 | leftCandidates.addAll(Sets.intersection(dependency.getKey(), targetAttributes)); | ||
81 | } | ||
82 | |||
83 | // Compute an initial list of nontrivial projected dependencies - it does not have to be minimal yet | ||
84 | Map<Set<A>, Set<A>> initialDependencies = new HashMap<Set<A>, Set<A>>(); | ||
85 | for (Set<A> leftSet : Sets.powerSet(leftCandidates)) { | ||
86 | Set<A> rightSet = Sets.intersection(closureOf(leftSet, originalDependencies), targetAttributes); | ||
87 | if (!isTrivial(leftSet, rightSet)) { | ||
88 | initialDependencies.put(leftSet, rightSet); | ||
89 | } | ||
90 | } | ||
91 | // Don't forget to include constants! | ||
92 | Set<A> constants = Sets.intersection(closureOf(Collections.<A>emptySet(), originalDependencies), targetAttributes); | ||
93 | if (! constants.isEmpty()) { | ||
94 | initialDependencies.put(Collections.<A>emptySet(), constants); | ||
95 | } | ||
96 | |||
97 | // Omit those dependencies where the LHS has superfluous attributes | ||
98 | Map<Set<A>, Set<A>> solidDependencies = new HashMap<Set<A>, Set<A>>(); | ||
99 | for (Entry<Set<A>, Set<A>> dependency : initialDependencies.entrySet()) { | ||
100 | Set<A> leftSet = dependency.getKey(); | ||
101 | Set<A> rightSet = dependency.getValue(); | ||
102 | boolean solid = true; | ||
103 | for (A skipped : leftSet) { // what if we skip one attribute from the left set? | ||
104 | Set<A> singleton = Collections.singleton(skipped); | ||
105 | Set<A> candidate = Sets.difference(leftSet, singleton); | ||
106 | Set<A> rightCandidate = initialDependencies.get(candidate); | ||
107 | if (rightCandidate != null) { | ||
108 | if (Sets.union(rightCandidate, singleton).containsAll(rightSet)) { | ||
109 | solid = false; | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | if (solid) { | ||
115 | solidDependencies.put(leftSet, rightSet); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | // TODO perform proper minimization, | ||
120 | // see e.g. page 45 in http://www.cs.ubc.ca/~hkhosrav/db/slides/03.design%20theory.pdf | ||
121 | |||
122 | return Collections.unmodifiableMap(solidDependencies); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Adds a given dependency to a mutable accumulator. | ||
127 | * @since 1.5 | ||
128 | */ | ||
129 | public static <A> void includeDependency(Map<Set<A>, Set<A>> accumulator, Set<A> left, Set<A> right) { | ||
130 | Set<A> accumulatorRights = accumulator.computeIfAbsent(left, l -> new HashSet<>()); | ||
131 | accumulatorRights.addAll(right); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Adds all given dependencies to a mutable accumulator. | ||
136 | * @since 1.5 | ||
137 | */ | ||
138 | public static <A> void includeDependencies(Map<Set<A>, Set<A>> accumulator, Map<Set<A>, Set<A>> additionalDependencies) { | ||
139 | for (Entry<Set<A>, Set<A>> entry : additionalDependencies.entrySet()) { | ||
140 | includeDependency(accumulator, entry.getKey(), entry.getValue()); | ||
141 | } | ||
142 | } | ||
143 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java new file mode 100644 index 00000000..b4f848a7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java | |||
@@ -0,0 +1,62 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.planning.helpers; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | import java.util.function.BiFunction; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.Accuracy; | ||
16 | |||
17 | /** | ||
18 | * Helpers dealing with optionally present statistics information | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * @since 2.1 | ||
22 | * | ||
23 | */ | ||
24 | public class StatisticsHelper { | ||
25 | |||
26 | private StatisticsHelper() { | ||
27 | // Hidden utility class constructor | ||
28 | } | ||
29 | |||
30 | public static Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy, | ||
31 | BiFunction<TupleMask, Accuracy, Optional<Long>> estimateCardinality) | ||
32 | { | ||
33 | if (groupMask.isIdentity()) return Optional.of(1.0); | ||
34 | |||
35 | Accuracy numeratorAccuracy = requiredAccuracy; | ||
36 | Accuracy denominatorAccuracy = requiredAccuracy.reciprocal(); | ||
37 | TupleMask identityMask = TupleMask.identity(groupMask.sourceWidth); | ||
38 | |||
39 | Optional<Long> totalCountEstimate = estimateCardinality.apply(identityMask, numeratorAccuracy); | ||
40 | Optional<Long> bucketCountEstimate = estimateCardinality.apply(groupMask, denominatorAccuracy); | ||
41 | |||
42 | return totalCountEstimate.flatMap(matchCount -> | ||
43 | bucketCountEstimate.map(bucketCount -> | ||
44 | bucketCount == 0L ? 0L : ((double) matchCount) / bucketCount | ||
45 | )); | ||
46 | } | ||
47 | |||
48 | public static Optional<Double> min(Optional<Double> a, Optional<Double> b) { | ||
49 | if (b.isPresent()) { | ||
50 | if (a.isPresent()) { | ||
51 | return Optional.of(Math.min(a.get(), b.get())); | ||
52 | } else return b; | ||
53 | } else return a; | ||
54 | } | ||
55 | public static Optional<Double> min(Optional<Double> a, double b) { | ||
56 | if (a.isPresent()) { | ||
57 | return Optional.of(Math.min(a.get(), b)); | ||
58 | } else return Optional.of(b); | ||
59 | } | ||
60 | |||
61 | |||
62 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java new file mode 100644 index 00000000..926a591f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java | |||
@@ -0,0 +1,217 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.planning.helpers; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.LinkedList; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | import java.util.Queue; | ||
19 | import java.util.Set; | ||
20 | import java.util.stream.Collectors; | ||
21 | |||
22 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
23 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * @author Tamas Szabo | ||
32 | */ | ||
33 | public class TypeHelper { | ||
34 | |||
35 | private TypeHelper() { | ||
36 | // Hiding constructor for utility class | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Collects the type constraints for the specified collection of variables. The type constraints consist of the | ||
41 | * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with | ||
42 | * through equalities. | ||
43 | * | ||
44 | * @param variables | ||
45 | * the variables in question | ||
46 | * @param constraints | ||
47 | * the constraints in the pattern body | ||
48 | * @param context | ||
49 | * the query meta context | ||
50 | * @return the mapping from variable to set of type constraints | ||
51 | * @since 1.6 | ||
52 | */ | ||
53 | public static Map<PVariable, Set<IInputKey>> inferUnaryTypesFor(Iterable<PVariable> variables, | ||
54 | Set<PConstraint> constraints, IQueryMetaContext context) { | ||
55 | Map<PVariable, Set<TypeJudgement>> typeMap = TypeHelper.inferUnaryTypes(constraints, context); | ||
56 | return inferUnaryTypesFor(variables, typeMap); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Collects the type constraints for the specified collection of variables. The type constraints consist of the | ||
61 | * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with | ||
62 | * through equalities. | ||
63 | * | ||
64 | * The method accepts a type map which is the result of the basic type inference from the | ||
65 | * {@link TypeHelper.inferUnaryTypes} method. The purpose of this method is that the type map can be reused across | ||
66 | * several calls to this method. | ||
67 | * | ||
68 | * @param variables | ||
69 | * the variables in question | ||
70 | * @param typeMap | ||
71 | * the type map of inference results | ||
72 | * @return the mapping from variable to set of type constraints | ||
73 | * @since 1.6 | ||
74 | */ | ||
75 | public static Map<PVariable, Set<IInputKey>> inferUnaryTypesFor(Iterable<PVariable> variables, | ||
76 | Map<PVariable, Set<TypeJudgement>> typeMap) { | ||
77 | Map<PVariable, Set<IInputKey>> result = new HashMap<PVariable, Set<IInputKey>>(); | ||
78 | |||
79 | for (PVariable original : variables) { | ||
80 | // it can happen that the variable was unified into an other one due to equalities | ||
81 | Set<IInputKey> keys = new HashSet<IInputKey>(); | ||
82 | PVariable current = original; | ||
83 | |||
84 | while (current != null) { | ||
85 | Set<TypeJudgement> judgements = typeMap.get(current); | ||
86 | if (judgements != null) { | ||
87 | for (TypeJudgement judgement : judgements) { | ||
88 | keys.add(judgement.getInputKey()); | ||
89 | } | ||
90 | } | ||
91 | current = current.getDirectUnifiedInto(); | ||
92 | } | ||
93 | |||
94 | result.put(original, keys); | ||
95 | } | ||
96 | |||
97 | return result; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Infers unary type information for variables, based on the given constraints. | ||
102 | * | ||
103 | * Subsumptions are not taken into account. | ||
104 | * | ||
105 | * @param constraints | ||
106 | * the set of constraints to extract type info from | ||
107 | */ | ||
108 | public static Map<PVariable, Set<TypeJudgement>> inferUnaryTypes(Set<PConstraint> constraints, | ||
109 | IQueryMetaContext context) { | ||
110 | Set<TypeJudgement> equivalentJudgements = getDirectJudgements(constraints, context); | ||
111 | Set<TypeJudgement> impliedJudgements = typeClosure(equivalentJudgements, context); | ||
112 | |||
113 | Map<PVariable, Set<TypeJudgement>> results = new HashMap<PVariable, Set<TypeJudgement>>(); | ||
114 | for (TypeJudgement typeJudgement : impliedJudgements) { | ||
115 | final IInputKey inputKey = typeJudgement.getInputKey(); | ||
116 | if (inputKey.getArity() == 1) { | ||
117 | PVariable variable = (PVariable) typeJudgement.getVariablesTuple().get(0); | ||
118 | Set<TypeJudgement> inferredTypes = results.computeIfAbsent(variable, v -> new HashSet<>()); | ||
119 | inferredTypes.add(typeJudgement); | ||
120 | } | ||
121 | } | ||
122 | return results; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Gets direct judgements reported by constraints. No closure is applied yet. | ||
127 | */ | ||
128 | public static Set<TypeJudgement> getDirectJudgements(Set<PConstraint> constraints, IQueryMetaContext context) { | ||
129 | Set<TypeJudgement> equivalentJudgements = new HashSet<TypeJudgement>(); | ||
130 | for (PConstraint pConstraint : constraints) { | ||
131 | if (pConstraint instanceof ITypeInfoProviderConstraint) { | ||
132 | equivalentJudgements.addAll(((ITypeInfoProviderConstraint) pConstraint).getImpliedJudgements(context)); | ||
133 | } | ||
134 | } | ||
135 | return equivalentJudgements; | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Calculates the closure of a set of type judgements, with respect to supertyping. | ||
140 | * | ||
141 | * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes | ||
142 | */ | ||
143 | public static Set<TypeJudgement> typeClosure(Set<TypeJudgement> typesToClose, IQueryMetaContext context) { | ||
144 | return typeClosure(Collections.<TypeJudgement> emptySet(), typesToClose, context); | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * Calculates the closure of a set of type judgements (with respect to supertyping), where the closure has been | ||
149 | * calculated before for a given base set, but not for a separate delta set. | ||
150 | * <p> | ||
151 | * Precondition: the set (typesToClose MINUS delta) is already closed w.r.t. supertyping. | ||
152 | * | ||
153 | * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes | ||
154 | * @since 1.6 | ||
155 | */ | ||
156 | public static Set<TypeJudgement> typeClosure(Set<TypeJudgement> preclosedBaseSet, Set<TypeJudgement> delta, | ||
157 | IQueryMetaContext context) { | ||
158 | Queue<TypeJudgement> queue = delta.stream().filter(input -> !preclosedBaseSet.contains(input)).collect(Collectors.toCollection(LinkedList::new)); | ||
159 | if (queue.isEmpty()) | ||
160 | return preclosedBaseSet; | ||
161 | |||
162 | Set<TypeJudgement> closure = new HashSet<TypeJudgement>(preclosedBaseSet); | ||
163 | |||
164 | Map<TypeJudgement, Set<TypeJudgement>> conditionalImplications = new HashMap<>(); | ||
165 | for (TypeJudgement typeJudgement : closure) { | ||
166 | conditionalImplications.putAll(typeJudgement.getConditionalImpliedJudgements(context)); | ||
167 | } | ||
168 | |||
169 | do { | ||
170 | TypeJudgement deltaType = queue.poll(); | ||
171 | if (closure.add(deltaType)) { | ||
172 | // direct implications | ||
173 | queue.addAll(deltaType.getDirectlyImpliedJudgements(context)); | ||
174 | |||
175 | // conditional implications, source key processed before, this is the condition key | ||
176 | final Set<TypeJudgement> implicationSet = conditionalImplications.get(deltaType); | ||
177 | if (implicationSet != null) { | ||
178 | queue.addAll(implicationSet); | ||
179 | } | ||
180 | |||
181 | // conditional implications, this is the source key | ||
182 | Map<TypeJudgement, Set<TypeJudgement>> deltaConditionalImplications = deltaType | ||
183 | .getConditionalImpliedJudgements(context); | ||
184 | for (Entry<TypeJudgement, Set<TypeJudgement>> entry : deltaConditionalImplications.entrySet()) { | ||
185 | if (closure.contains(entry.getKey())) { | ||
186 | // condition processed before | ||
187 | queue.addAll(entry.getValue()); | ||
188 | } else { | ||
189 | // condition not processed yet | ||
190 | conditionalImplications.computeIfAbsent(entry.getKey(), key -> new HashSet<>()) | ||
191 | .addAll(entry.getValue()); | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | } while (!queue.isEmpty()); | ||
196 | |||
197 | return closure; | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * Calculates a remainder set of types from a larger set, that are not subsumed by a given set of subsuming types. | ||
202 | * | ||
203 | * @param subsumableTypes | ||
204 | * a set of types from which some may be implied by the subsuming types | ||
205 | * @param subsumingTypes | ||
206 | * a set of types that may imply some of the subsuming types | ||
207 | * @return the collection of types in subsumableTypes that are NOT identical to or supertypes of any type in | ||
208 | * subsumingTypes. | ||
209 | */ | ||
210 | public static Set<TypeJudgement> subsumeTypes(Set<TypeJudgement> subsumableTypes, Set<TypeJudgement> subsumingTypes, | ||
211 | IQueryMetaContext context) { | ||
212 | Set<TypeJudgement> closure = typeClosure(subsumingTypes, context); | ||
213 | Set<TypeJudgement> subsumed = new HashSet<TypeJudgement>(subsumableTypes); | ||
214 | subsumed.removeAll(closure); | ||
215 | return subsumed; | ||
216 | } | ||
217 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java new file mode 100644 index 00000000..2c285b54 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
17 | |||
18 | /** | ||
19 | * Represents a constraint application on a single parent SubPlan. | ||
20 | * <p> Either a "selection" filter operation according to a deferred PConstraint (or transform in case of eval/aggregate), or | ||
21 | * alternatively a shorthand for PJoin + a PEnumerate on the right input for an enumerable PConstraint. | ||
22 | * | ||
23 | * <p> <b>WARNING</b>: if there are coinciding variables in the variable tuple of the enumerable constraint, | ||
24 | * it is the responsibility of the compiler to check them for equality. | ||
25 | * | ||
26 | * @author Bergmann Gabor | ||
27 | * | ||
28 | */ | ||
29 | public class PApply extends POperation { | ||
30 | |||
31 | private PConstraint pConstraint; | ||
32 | |||
33 | public PApply(PConstraint pConstraint) { | ||
34 | super(); | ||
35 | this.pConstraint = pConstraint; | ||
36 | } | ||
37 | public PConstraint getPConstraint() { | ||
38 | return pConstraint; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public String getShortName() { | ||
43 | return String.format("APPLY_%s", pConstraint.toString()); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
48 | return Collections.singleton(pConstraint); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public int numParentSubPlans() { | ||
53 | return 1; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public void checkConsistency(SubPlan subPlan) { | ||
58 | super.checkConsistency(subPlan); | ||
59 | for (SubPlan parentPlan : subPlan.getParentPlans()) | ||
60 | Preconditions.checkArgument(!parentPlan.getAllEnforcedConstraints().contains(pConstraint), | ||
61 | "Double-checking constraint %s", pConstraint); | ||
62 | // TODO obtain context? | ||
63 | //if (pConstraint instanceof DeferredPConstraint) | ||
64 | // Preconditions.checkArgument(((DeferredPConstraint) pConstraint).isReadyAt(subPlan, context)) | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public int hashCode() { | ||
69 | final int prime = 31; | ||
70 | int result = 1; | ||
71 | result = prime | ||
72 | * result | ||
73 | + ((pConstraint == null) ? 0 : pConstraint | ||
74 | .hashCode()); | ||
75 | return result; | ||
76 | } | ||
77 | @Override | ||
78 | public boolean equals(Object obj) { | ||
79 | if (this == obj) | ||
80 | return true; | ||
81 | if (obj == null) | ||
82 | return false; | ||
83 | if (!(obj instanceof PApply)) | ||
84 | return false; | ||
85 | PApply other = (PApply) obj; | ||
86 | if (pConstraint == null) { | ||
87 | if (other.pConstraint != null) | ||
88 | return false; | ||
89 | } else if (!pConstraint.equals(other.pConstraint)) | ||
90 | return false; | ||
91 | return true; | ||
92 | } | ||
93 | |||
94 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java new file mode 100644 index 00000000..a975d50c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
16 | |||
17 | /** | ||
18 | * Represents a base relation defined by the instance set of an enumerable PConstraint; there are no parent SubPlans. | ||
19 | * | ||
20 | * <p> <b>WARNING</b>: if there are coinciding variables in the variable tuple of the enumerable constraint, | ||
21 | * it is the responsibility of the compiler to check them for equality. | ||
22 | * @author Bergmann Gabor | ||
23 | * | ||
24 | */ | ||
25 | public class PEnumerate extends POperation { | ||
26 | |||
27 | EnumerablePConstraint enumerablePConstraint; | ||
28 | |||
29 | public PEnumerate(EnumerablePConstraint enumerablePConstraint) { | ||
30 | super(); | ||
31 | this.enumerablePConstraint = enumerablePConstraint; | ||
32 | } | ||
33 | public EnumerablePConstraint getEnumerablePConstraint() { | ||
34 | return enumerablePConstraint; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
39 | return Collections.singleton(enumerablePConstraint); | ||
40 | } | ||
41 | @Override | ||
42 | public int numParentSubPlans() { | ||
43 | return 0; | ||
44 | } | ||
45 | @Override | ||
46 | public String getShortName() { | ||
47 | return enumerablePConstraint.toString(); | ||
48 | } | ||
49 | @Override | ||
50 | public int hashCode() { | ||
51 | final int prime = 31; | ||
52 | int result = 1; | ||
53 | result = prime | ||
54 | * result | ||
55 | + ((enumerablePConstraint == null) ? 0 : enumerablePConstraint | ||
56 | .hashCode()); | ||
57 | return result; | ||
58 | } | ||
59 | @Override | ||
60 | public boolean equals(Object obj) { | ||
61 | if (this == obj) | ||
62 | return true; | ||
63 | if (obj == null) | ||
64 | return false; | ||
65 | if (!(obj instanceof PEnumerate)) | ||
66 | return false; | ||
67 | PEnumerate other = (PEnumerate) obj; | ||
68 | if (enumerablePConstraint == null) { | ||
69 | if (other.enumerablePConstraint != null) | ||
70 | return false; | ||
71 | } else if (!enumerablePConstraint.equals(other.enumerablePConstraint)) | ||
72 | return false; | ||
73 | return true; | ||
74 | } | ||
75 | |||
76 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java new file mode 100644 index 00000000..10e0a85a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.planning.operations; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | |||
16 | /** | ||
17 | * Represents a natural join of two parent SubPlans. | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public class PJoin extends POperation { | ||
22 | |||
23 | // // TODO leave here? is this a problem in equivalnece checking? | ||
24 | // private Set<PVariable> onVariables; | ||
25 | |||
26 | public PJoin(/*Set<PVariable> onVariables*/) { | ||
27 | super(); | ||
28 | //this.onVariables = new HashSet<PVariable>(onVariables); | ||
29 | } | ||
30 | // public Set<PVariable> getOnVariables() { | ||
31 | // return onVariables; | ||
32 | // } | ||
33 | |||
34 | @Override | ||
35 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
36 | return Collections.emptySet(); | ||
37 | } | ||
38 | @Override | ||
39 | public int numParentSubPlans() { | ||
40 | return 2; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public String getShortName() { | ||
45 | return "JOIN"; //String.format("JOIN_{%s}", Joiner.on(",").join(onVariables)); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public int hashCode() { | ||
50 | return getClass().hashCode(); | ||
51 | } | ||
52 | @Override | ||
53 | public boolean equals(Object obj) { | ||
54 | if (this == obj) | ||
55 | return true; | ||
56 | if (obj == null) | ||
57 | return false; | ||
58 | if (!(obj instanceof PJoin)) | ||
59 | return false; | ||
60 | return true; | ||
61 | } | ||
62 | |||
63 | |||
64 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java new file mode 100644 index 00000000..e71cf217 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java | |||
@@ -0,0 +1,52 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.planning.operations; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
16 | |||
17 | /** | ||
18 | * Abstract superclass for representing a high-level query evaluation operation. | ||
19 | * | ||
20 | * <p> Subclasses correspond to various POperations modeled after relational algebra. | ||
21 | * | ||
22 | * @author Bergmann Gabor | ||
23 | * | ||
24 | */ | ||
25 | public abstract class POperation { | ||
26 | |||
27 | /** | ||
28 | * Newly enforced constraints | ||
29 | */ | ||
30 | public abstract Set<? extends PConstraint> getDeltaConstraints(); | ||
31 | |||
32 | public abstract String getShortName(); | ||
33 | |||
34 | /** | ||
35 | * @return the number of SubPlans that must be specified as parents | ||
36 | */ | ||
37 | public abstract int numParentSubPlans(); | ||
38 | |||
39 | /** | ||
40 | * Checks whether this constraint can be properly applied at the given SubPlan. | ||
41 | */ | ||
42 | public void checkConsistency(SubPlan subPlan) { | ||
43 | Preconditions.checkArgument(this == subPlan.getOperation(), "POperation misalignment"); | ||
44 | Preconditions.checkArgument(subPlan.getParentPlans().size() == numParentSubPlans(), "Incorrect number of parent SubPlans"); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public String toString() { | ||
49 | return getShortName(); | ||
50 | } | ||
51 | |||
52 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java new file mode 100644 index 00000000..d0539b2c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java | |||
@@ -0,0 +1,109 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.planning.operations; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
21 | |||
22 | /** | ||
23 | * Represents a projection of a single parent SubPlan onto a limited set of variables. | ||
24 | * <p> May optionally prescribe an ordering of variables (List, as opposed to Set). | ||
25 | * | ||
26 | * @author Bergmann Gabor | ||
27 | * | ||
28 | */ | ||
29 | public class PProject extends POperation { | ||
30 | |||
31 | private Collection<PVariable> toVariables; | ||
32 | private boolean ordered; | ||
33 | |||
34 | |||
35 | public PProject(Set<PVariable> toVariables) { | ||
36 | super(); | ||
37 | this.toVariables = toVariables; | ||
38 | this.ordered = false; | ||
39 | } | ||
40 | public PProject(List<PVariable> toVariables) { | ||
41 | super(); | ||
42 | this.toVariables = toVariables; | ||
43 | this.ordered = true; | ||
44 | } | ||
45 | |||
46 | public Collection<PVariable> getToVariables() { | ||
47 | return toVariables; | ||
48 | } | ||
49 | public boolean isOrdered() { | ||
50 | return ordered; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
55 | return Collections.emptySet(); | ||
56 | } | ||
57 | @Override | ||
58 | public int numParentSubPlans() { | ||
59 | return 1; | ||
60 | } | ||
61 | @Override | ||
62 | public void checkConsistency(SubPlan subPlan) { | ||
63 | super.checkConsistency(subPlan); | ||
64 | final SubPlan parentPlan = subPlan.getParentPlans().get(0); | ||
65 | |||
66 | Preconditions.checkArgument(parentPlan.getVisibleVariables().containsAll(toVariables), | ||
67 | () -> toVariables.stream() | ||
68 | .filter(input -> !parentPlan.getVisibleVariables().contains(input)).map(PVariable::getName) | ||
69 | .collect(Collectors.joining(",", "Variables missing from project: ", ""))); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public String getShortName() { | ||
74 | return String.format("PROJECT%s_{%s}", ordered? "!" : "", | ||
75 | toVariables.stream().map(PVariable::getName).collect(Collectors.joining(","))); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public int hashCode() { | ||
80 | final int prime = 31; | ||
81 | int result = 1; | ||
82 | result = prime * result + (ordered ? 1231 : 1237); | ||
83 | result = prime * result | ||
84 | + ((toVariables == null) ? 0 : toVariables.hashCode()); | ||
85 | return result; | ||
86 | } | ||
87 | @Override | ||
88 | public boolean equals(Object obj) { | ||
89 | if (this == obj) | ||
90 | return true; | ||
91 | if (obj == null) | ||
92 | return false; | ||
93 | if (!(obj instanceof PProject)) | ||
94 | return false; | ||
95 | PProject other = (PProject) obj; | ||
96 | if (ordered != other.ordered) | ||
97 | return false; | ||
98 | if (toVariables == null) { | ||
99 | if (other.toVariables != null) | ||
100 | return false; | ||
101 | } else if (!toVariables.equals(other.toVariables)) | ||
102 | return false; | ||
103 | return true; | ||
104 | } | ||
105 | |||
106 | |||
107 | |||
108 | |||
109 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java new file mode 100644 index 00000000..9e6ea10e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java | |||
@@ -0,0 +1,90 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.planning.operations; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
19 | |||
20 | /** | ||
21 | * No constraints, and no parent SubPlan, just a (possibly empty) set of a priori known (input) variables. Satisfied by a single tuple. | ||
22 | * | ||
23 | * <p> Can also be used without a priori variables, | ||
24 | * e.g. as a "virtual parent" in extreme cases, | ||
25 | * such as <code>pattern foo(Bar) = {Bar = eval (3*4)} </code> | ||
26 | * | ||
27 | * @author Bergmann Gabor | ||
28 | * | ||
29 | */ | ||
30 | public class PStart extends POperation { | ||
31 | |||
32 | private Set<PVariable> aPrioriVariables; | ||
33 | |||
34 | |||
35 | public PStart(Set<PVariable> aPrioriVariables) { | ||
36 | super(); | ||
37 | this.aPrioriVariables = aPrioriVariables; | ||
38 | } | ||
39 | public PStart(PVariable... aPrioriVariables) { | ||
40 | this(new HashSet<PVariable>(Arrays.asList(aPrioriVariables))); | ||
41 | } | ||
42 | public Set<PVariable> getAPrioriVariables() { | ||
43 | return aPrioriVariables; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public String getShortName() { | ||
48 | return aPrioriVariables.stream().map(PVariable::getName).collect(Collectors.joining(",", "START_{", "}")); | ||
49 | } | ||
50 | @Override | ||
51 | public int numParentSubPlans() { | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public Set<? extends PConstraint> getDeltaConstraints() { | ||
57 | return Collections.emptySet(); | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public int hashCode() { | ||
62 | final int prime = 31; | ||
63 | int result = 1; | ||
64 | result = prime | ||
65 | * result | ||
66 | + ((aPrioriVariables == null) ? 0 : aPrioriVariables.hashCode()); | ||
67 | return result; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public boolean equals(Object obj) { | ||
72 | if (this == obj) | ||
73 | return true; | ||
74 | if (obj == null) | ||
75 | return false; | ||
76 | if (!(obj instanceof PStart)) | ||
77 | return false; | ||
78 | PStart other = (PStart) obj; | ||
79 | if (aPrioriVariables == null) { | ||
80 | if (other.aPrioriVariables != null) | ||
81 | return false; | ||
82 | } else if (!aPrioriVariables.equals(other.aPrioriVariables)) | ||
83 | return false; | ||
84 | return true; | ||
85 | } | ||
86 | |||
87 | |||
88 | |||
89 | |||
90 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java new file mode 100644 index 00000000..eda4aa25 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
13 | |||
14 | import java.util.Collections; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | import java.util.concurrent.atomic.AtomicInteger; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | */ | ||
24 | public abstract class BasePConstraint implements PConstraint { | ||
25 | |||
26 | |||
27 | protected PBody pBody; | ||
28 | private final Set<PVariable> affectedVariables; | ||
29 | |||
30 | |||
31 | private final int sequentialID = nextID.getAndIncrement(); | ||
32 | |||
33 | // Use a static atomic integer to avoid race conditions when creating new constraints. | ||
34 | private static AtomicInteger nextID = new AtomicInteger(0); | ||
35 | |||
36 | public BasePConstraint(PBody pBody, Set<PVariable> affectedVariables) { | ||
37 | super(); | ||
38 | this.pBody = pBody; | ||
39 | this.affectedVariables = new HashSet<PVariable>(affectedVariables); | ||
40 | |||
41 | for (PVariable pVariable : affectedVariables) { | ||
42 | pVariable.refer(this); | ||
43 | } | ||
44 | pBody.registerConstraint(this); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public String toString() { | ||
49 | return "PC[" + getClass().getSimpleName() + ":" + toStringRest() + "]"; | ||
50 | } | ||
51 | |||
52 | protected abstract String toStringRest(); | ||
53 | |||
54 | @Override | ||
55 | public Set<PVariable> getAffectedVariables() { | ||
56 | return affectedVariables; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
61 | return Collections.emptyMap(); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public void replaceVariable(PVariable obsolete, PVariable replacement) { | ||
66 | pBody.checkMutability(); | ||
67 | if (affectedVariables.remove(obsolete)) { | ||
68 | affectedVariables.add(replacement); | ||
69 | obsolete.unrefer(this); | ||
70 | replacement.refer(this); | ||
71 | doReplaceVariable(obsolete, replacement); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | protected abstract void doReplaceVariable(PVariable obsolete, PVariable replacement); | ||
76 | |||
77 | @Override | ||
78 | public void delete() { | ||
79 | pBody.checkMutability(); | ||
80 | for (PVariable pVariable : affectedVariables) { | ||
81 | pVariable.unrefer(this); | ||
82 | } | ||
83 | pBody.unregisterConstraint(this); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void checkSanity() { | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * For backwards compatibility. Equivalent to {@link #getBody()} | ||
92 | */ | ||
93 | public PBody getPSystem() { | ||
94 | return pBody; | ||
95 | } | ||
96 | /** | ||
97 | * @since 2.1 | ||
98 | */ | ||
99 | @Override | ||
100 | public PBody getBody() { | ||
101 | return pBody; | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public int getMonotonousID() { | ||
106 | return sequentialID; | ||
107 | } | ||
108 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java new file mode 100644 index 00000000..d2bf088c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
16 | |||
17 | /** | ||
18 | * Any constraint that can only be checked on certain SubPlans (e.g. those plans that already contain some variables). | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public abstract class DeferredPConstraint extends BasePConstraint { | ||
24 | |||
25 | public DeferredPConstraint(PBody pBody, Set<PVariable> affectedVariables) { | ||
26 | super(pBody, affectedVariables); | ||
27 | } | ||
28 | |||
29 | public abstract boolean isReadyAt(SubPlan plan, IQueryMetaContext context); | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java new file mode 100644 index 00000000..9129aa47 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java | |||
@@ -0,0 +1,59 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | |||
16 | /** | ||
17 | * A constraint for which all satisfying tuples of variable values can be enumerated at any point during run-time. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public abstract class EnumerablePConstraint extends BasePConstraint { | ||
23 | protected Tuple variablesTuple; | ||
24 | |||
25 | protected EnumerablePConstraint(PBody pBody, Tuple variablesTuple) { | ||
26 | super(pBody, variablesTuple.<PVariable> getDistinctElements()); | ||
27 | this.variablesTuple = variablesTuple; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
32 | variablesTuple = variablesTuple.replaceAll(obsolete, replacement); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | protected String toStringRest() { | ||
37 | String stringRestRest = toStringRestRest(); | ||
38 | String tupleString = "@" + variablesTuple.toString(); | ||
39 | return stringRestRest == null ? tupleString : ":" + stringRestRest + tupleString; | ||
40 | } | ||
41 | |||
42 | protected String toStringRestRest() { | ||
43 | return null; | ||
44 | } | ||
45 | |||
46 | public Tuple getVariablesTuple() { | ||
47 | return variablesTuple; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public Set<PVariable> getDeducedVariables() { | ||
52 | return getAffectedVariables(); | ||
53 | } | ||
54 | |||
55 | public PVariable getVariableInTuple(int index) { | ||
56 | return (PVariable) this.variablesTuple.get(index); | ||
57 | } | ||
58 | |||
59 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java new file mode 100644 index 00000000..686999f7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java | |||
@@ -0,0 +1,42 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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; | ||
10 | |||
11 | /** | ||
12 | * An expression evaluator is used to execute arbitrary Java code during pattern matching. In order to include the | ||
13 | * evaluation in the planning seemlessly it is expected from the evaluator implementors to report all used PVariables by | ||
14 | * name. | ||
15 | * | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * | ||
18 | */ | ||
19 | public interface IExpressionEvaluator { | ||
20 | |||
21 | /** | ||
22 | * A textual description of the expression. Used only for debug purposes, but must not be null. | ||
23 | */ | ||
24 | String getShortDescription(); | ||
25 | |||
26 | /** | ||
27 | * All input parameter names should be reported correctly. | ||
28 | */ | ||
29 | Iterable<String> getInputParameterNames(); | ||
30 | |||
31 | /** | ||
32 | * The expression evaluator code | ||
33 | * | ||
34 | * @param provider | ||
35 | * the value provider is an engine-specific way of reading internal variable tuples to evaluate the | ||
36 | * expression with | ||
37 | * @return the result of the expression: in case of predicate evaluation the return value must be true or false; | ||
38 | * otherwise the result can be an arbitrary object. No null values should be returned. | ||
39 | * @throws Exception | ||
40 | */ | ||
41 | Object evaluateExpression(IValueProvider provider) throws Exception; | ||
42 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java new file mode 100644 index 00000000..8f647c64 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2022, Tamas Szabo, GitHub | ||
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; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | |||
15 | /** | ||
16 | * A {@link PConstraint} that implements this interface refers to a list of PQueries. | ||
17 | * | ||
18 | * @author Tamas Szabo | ||
19 | * @since 2.8 | ||
20 | * | ||
21 | */ | ||
22 | public interface IMultiQueryReference { | ||
23 | |||
24 | Collection<PQuery> getReferredQueries(); | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java new file mode 100644 index 00000000..9ee05b39 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * A {@link PConstraint} that implements this interface refers to a {@link PQuery}. | ||
18 | * | ||
19 | * @author Zoltan Ujhelyi | ||
20 | * | ||
21 | */ | ||
22 | public interface IQueryReference extends IMultiQueryReference { | ||
23 | |||
24 | PQuery getReferredQuery(); | ||
25 | |||
26 | /** | ||
27 | * @since 2.8 | ||
28 | */ | ||
29 | @Override | ||
30 | default List<PQuery> getReferredQueries() { | ||
31 | return Collections.singletonList(getReferredQuery()); | ||
32 | } | ||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java new file mode 100644 index 00000000..e4c396d8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java | |||
@@ -0,0 +1,47 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2022, Tamas Szabo, GitHub | ||
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; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | |||
16 | /** | ||
17 | * Implementations of this interface take an arbitrary number of input relations with their contents and compute the | ||
18 | * tuples of a single output relation. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * @since 2.8 | ||
22 | * | ||
23 | */ | ||
24 | public interface IRelationEvaluator { | ||
25 | |||
26 | /** | ||
27 | * A textual description of the evaluator. Used only for debug purposes, but must not be null. | ||
28 | */ | ||
29 | String getShortDescription(); | ||
30 | |||
31 | /** | ||
32 | * The relation evaluator code. For performance reasons, it is expected that the returned set is a mutable | ||
33 | * collection, and the caller must be allowed to actually perform mutations! | ||
34 | */ | ||
35 | Set<Tuple> evaluateRelation(List<Set<Tuple>> inputs) throws Exception; | ||
36 | |||
37 | /** | ||
38 | * For each input relation that this evaluator requires, this method returns the expected arities of the relations in order. | ||
39 | */ | ||
40 | List<Integer> getInputArities(); | ||
41 | |||
42 | /** | ||
43 | * Returns the arity of the output relation that this evaluator computes. | ||
44 | */ | ||
45 | int getOutputArity(); | ||
46 | |||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java new file mode 100644 index 00000000..b72035a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java | |||
@@ -0,0 +1,65 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | import java.util.Set; | ||
21 | |||
22 | /** | ||
23 | * Common superinterface of enumerable and deferred type constraints. | ||
24 | * @author Bergmann Gabor | ||
25 | * | ||
26 | */ | ||
27 | public interface ITypeConstraint extends ITypeInfoProviderConstraint { | ||
28 | |||
29 | public abstract TypeJudgement getEquivalentJudgement(); | ||
30 | |||
31 | /** | ||
32 | * Static internal utility class for implementations of {@link ITypeConstraint}s. | ||
33 | * @author Bergmann Gabor | ||
34 | */ | ||
35 | public static class TypeConstraintUtil { | ||
36 | |||
37 | private TypeConstraintUtil() { | ||
38 | // Hiding constructor for utility class | ||
39 | } | ||
40 | |||
41 | public static Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context, IInputKey inputKey, Tuple variablesTuple) { | ||
42 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
43 | |||
44 | Set<Entry<Set<Integer>, Set<Integer>>> dependencies = context.getFunctionalDependencies(inputKey).entrySet(); | ||
45 | for (Entry<Set<Integer>, Set<Integer>> dependency : dependencies) { | ||
46 | result.put( | ||
47 | transcribeVariables(dependency.getKey(), variablesTuple), | ||
48 | transcribeVariables(dependency.getValue(), variablesTuple) | ||
49 | ); | ||
50 | } | ||
51 | |||
52 | return result; | ||
53 | } | ||
54 | |||
55 | private static Set<PVariable> transcribeVariables(Set<Integer> indices, Tuple variablesTuple) { | ||
56 | Set<PVariable> result = new HashSet<PVariable>(); | ||
57 | for (Integer index : indices) { | ||
58 | result.add((PVariable) variablesTuple.get(index)); | ||
59 | } | ||
60 | return result; | ||
61 | } | ||
62 | |||
63 | } | ||
64 | |||
65 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java new file mode 100644 index 00000000..ff127d38 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java | |||
@@ -0,0 +1,28 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | |||
16 | /** | ||
17 | * @author Gabor Bergmann | ||
18 | * | ||
19 | */ | ||
20 | public interface ITypeInfoProviderConstraint extends PConstraint { | ||
21 | |||
22 | /** | ||
23 | * Returns type information implied by this constraint. | ||
24 | * | ||
25 | */ | ||
26 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context); | ||
27 | |||
28 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java new file mode 100644 index 00000000..d959adc4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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; | ||
10 | |||
11 | /** | ||
12 | * Helper interface to get values from a tuple of variables. All pattern matching engines are expected to implement this | ||
13 | * to handle their internal structures. | ||
14 | * | ||
15 | * @author Zoltan Ujhelyi | ||
16 | * | ||
17 | */ | ||
18 | public interface IValueProvider { | ||
19 | |||
20 | /** | ||
21 | * Returns the value of the selected variable | ||
22 | * @param variableName | ||
23 | * @return the value of the variable; never null | ||
24 | * @throws IllegalArgumentException if the variable is not defined | ||
25 | */ | ||
26 | Object getValue(String variableName); | ||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java new file mode 100644 index 00000000..a82d12ec --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java | |||
@@ -0,0 +1,56 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PProblem; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * Adds extra methods to the PQuery interface to initialize its contents. | ||
20 | * | ||
21 | * @author Zoltan Ujhelyi | ||
22 | * | ||
23 | */ | ||
24 | public interface InitializablePQuery extends PQuery { | ||
25 | |||
26 | /** | ||
27 | * Sets the query status. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. | ||
28 | * | ||
29 | * @param status the new status | ||
30 | */ | ||
31 | void setStatus(PQueryStatus status); | ||
32 | |||
33 | /** | ||
34 | * Adds a detected error. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. | ||
35 | * | ||
36 | * @param problem the new problem | ||
37 | */ | ||
38 | void addError(PProblem problem); | ||
39 | |||
40 | /** | ||
41 | * Sets up the bodies of the pattern. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED | ||
42 | * uninitialized}. | ||
43 | * | ||
44 | * @param bodies | ||
45 | * @throws ViatraQueryRuntimeException | ||
46 | */ | ||
47 | void initializeBodies(Set<PBody> bodies); | ||
48 | |||
49 | /** | ||
50 | * Adds an annotation to the specification. Only applicable if the pattern is still | ||
51 | * {@link PQueryStatus#UNINITIALIZED uninitialized}. | ||
52 | * | ||
53 | * @param annotation | ||
54 | */ | ||
55 | void addAnnotation(PAnnotation annotation); | ||
56 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java new file mode 100644 index 00000000..91eea817 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java | |||
@@ -0,0 +1,39 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | |||
14 | /** | ||
15 | * @author Gabor Bergmann | ||
16 | * | ||
17 | */ | ||
18 | public abstract class KeyedEnumerablePConstraint<KeyType> extends EnumerablePConstraint { | ||
19 | |||
20 | protected KeyType supplierKey; | ||
21 | |||
22 | public KeyedEnumerablePConstraint(PBody pBody, Tuple variablesTuple, | ||
23 | KeyType supplierKey) { | ||
24 | super(pBody, variablesTuple); | ||
25 | this.supplierKey = supplierKey; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | protected String toStringRestRest() { | ||
30 | return supplierKey == null ? "$any(null)" : keyToString(); | ||
31 | } | ||
32 | |||
33 | protected abstract String keyToString(); | ||
34 | |||
35 | public KeyType getSupplierKey() { | ||
36 | return supplierKey; | ||
37 | } | ||
38 | |||
39 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java new file mode 100644 index 00000000..c38dc23a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java | |||
@@ -0,0 +1,289 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.LinkedHashSet; | ||
16 | import java.util.List; | ||
17 | import java.util.Map; | ||
18 | import java.util.Set; | ||
19 | import java.util.WeakHashMap; | ||
20 | import java.util.stream.Collectors; | ||
21 | |||
22 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
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.util.Preconditions; | ||
30 | |||
31 | /** | ||
32 | * A set of constraints representing a pattern body | ||
33 | * | ||
34 | * @author Gabor Bergmann | ||
35 | * | ||
36 | */ | ||
37 | public class PBody implements PTraceable { | ||
38 | |||
39 | public static final String VIRTUAL_VARIABLE_PREFIX = ".virtual"; | ||
40 | private static final String VIRTUAL_VARIABLE_PATTERN = VIRTUAL_VARIABLE_PREFIX + "{%d}"; | ||
41 | |||
42 | private PQuery query; | ||
43 | |||
44 | /** | ||
45 | * If null, then parent query status is reused | ||
46 | */ | ||
47 | private PQueryStatus status = PQueryStatus.UNINITIALIZED; | ||
48 | |||
49 | private Set<PVariable> allVariables; | ||
50 | private Set<PVariable> uniqueVariables; | ||
51 | private List<ExportedParameter> symbolicParameters; | ||
52 | private Map<Object, PVariable> variablesByName; | ||
53 | private Set<PConstraint> constraints; | ||
54 | private int nextVirtualNodeID; | ||
55 | private PDisjunction containerDisjunction; | ||
56 | |||
57 | public PBody(PQuery query) { | ||
58 | super(); | ||
59 | this.query = query; | ||
60 | allVariables = new LinkedHashSet<>(); | ||
61 | uniqueVariables = new LinkedHashSet<>(); | ||
62 | variablesByName = new HashMap<>(); | ||
63 | constraints = new LinkedHashSet<>(); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * @return whether the submission of the new variable was successful | ||
68 | */ | ||
69 | private boolean addVariable(PVariable var) { | ||
70 | checkMutability(); | ||
71 | Object name = var.getName(); | ||
72 | if (!variablesByName.containsKey(name)) { | ||
73 | allVariables.add(var); | ||
74 | if (var.isUnique()) | ||
75 | uniqueVariables.add(var); | ||
76 | variablesByName.put(name, var); | ||
77 | return true; | ||
78 | } else { | ||
79 | return false; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Use this method to add a newly created constraint to the pSystem. | ||
85 | * | ||
86 | * @return whether the submission of the new constraint was successful | ||
87 | */ | ||
88 | boolean registerConstraint(PConstraint constraint) { | ||
89 | checkMutability(); | ||
90 | return constraints.add(constraint); | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * Use this method to remove an obsolete constraint from the pSystem. | ||
95 | * | ||
96 | * @return whether the removal of the constraint was successful | ||
97 | */ | ||
98 | boolean unregisterConstraint(PConstraint constraint) { | ||
99 | checkMutability(); | ||
100 | return constraints.remove(constraint); | ||
101 | } | ||
102 | |||
103 | @SuppressWarnings("unchecked") | ||
104 | public <ConstraintType> Set<ConstraintType> getConstraintsOfType(Class<ConstraintType> constraintClass) { | ||
105 | Set<ConstraintType> result = new HashSet<ConstraintType>(); | ||
106 | for (PConstraint pConstraint : constraints) { | ||
107 | if (constraintClass.isInstance(pConstraint)) | ||
108 | result.add((ConstraintType) pConstraint); | ||
109 | } | ||
110 | return result; | ||
111 | } | ||
112 | |||
113 | public PVariable newVirtualVariable() { | ||
114 | checkMutability(); | ||
115 | String name; | ||
116 | do { | ||
117 | |||
118 | name = String.format(VIRTUAL_VARIABLE_PATTERN, nextVirtualNodeID++); | ||
119 | } while (variablesByName.containsKey(name)); | ||
120 | PVariable var = new PVariable(this, name, true); | ||
121 | addVariable(var); | ||
122 | return var; | ||
123 | } | ||
124 | |||
125 | public PVariable newVirtualVariable(String name) { | ||
126 | checkMutability(); | ||
127 | Preconditions.checkArgument(!variablesByName.containsKey(name), "ID %s already used for a virtual variable", name); | ||
128 | PVariable var = new PVariable(this, name, true); | ||
129 | addVariable(var); | ||
130 | return var; | ||
131 | } | ||
132 | |||
133 | public PVariable newConstantVariable(Object value) { | ||
134 | checkMutability(); | ||
135 | PVariable virtual = newVirtualVariable(); | ||
136 | new ConstantValue(this, virtual, value); | ||
137 | return virtual; | ||
138 | } | ||
139 | |||
140 | public Set<PVariable> getAllVariables() { | ||
141 | return allVariables; | ||
142 | } | ||
143 | |||
144 | public Set<PVariable> getUniqueVariables() { | ||
145 | return uniqueVariables; | ||
146 | } | ||
147 | |||
148 | private PVariable getVariableByName(Object name) { | ||
149 | return variablesByName.get(name).getUnifiedIntoRoot(); | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Find a PVariable by name | ||
154 | * | ||
155 | * @param name | ||
156 | * @return the found variable | ||
157 | * @throws IllegalArgumentException | ||
158 | * if no PVariable is found with the selected name | ||
159 | */ | ||
160 | public PVariable getVariableByNameChecked(Object name) { | ||
161 | if (!variablesByName.containsKey(name)) | ||
162 | throw new IllegalArgumentException(String.format("Cannot find PVariable %s", name)); | ||
163 | return getVariableByName(name); | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Finds and returns a PVariable by name. If no PVariable exists with the name in the body, a new one is created. If | ||
168 | * the name of the variable starts with {@value #VIRTUAL_VARIABLE_PREFIX}, the created variable will be considered | ||
169 | * virtual. | ||
170 | * | ||
171 | * @param name | ||
172 | * @return a PVariable with the selected name; never null | ||
173 | */ | ||
174 | public PVariable getOrCreateVariableByName(String name) { | ||
175 | checkMutability(); | ||
176 | if (!variablesByName.containsKey(name)) { | ||
177 | addVariable(new PVariable(this, name, name.startsWith(VIRTUAL_VARIABLE_PREFIX))); | ||
178 | } | ||
179 | return getVariableByName(name); | ||
180 | } | ||
181 | |||
182 | public Set<PConstraint> getConstraints() { | ||
183 | return constraints; | ||
184 | } | ||
185 | |||
186 | public PQuery getPattern() { | ||
187 | return query; | ||
188 | } | ||
189 | |||
190 | void noLongerUnique(PVariable pVariable) { | ||
191 | assert (!pVariable.isUnique()); | ||
192 | uniqueVariables.remove(pVariable); | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Returns the symbolic parameters of the body. </p> | ||
197 | * | ||
198 | * <p> | ||
199 | * <strong>Warning</strong>: if two PVariables are unified, the returned list changes. If you want to have a stable | ||
200 | * version, consider using {@link #getSymbolicParameters()}. | ||
201 | * | ||
202 | * @return a non-null, but possibly empty list | ||
203 | */ | ||
204 | public List<PVariable> getSymbolicParameterVariables() { | ||
205 | return getSymbolicParameters().stream().map(ExportedParameter::getParameterVariable) | ||
206 | .collect(Collectors.toList()); | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * Returns the exported parameter constraints of the body. | ||
211 | * | ||
212 | * @return a non-null, but possibly empty list | ||
213 | */ | ||
214 | public List<ExportedParameter> getSymbolicParameters() { | ||
215 | if (symbolicParameters == null) | ||
216 | symbolicParameters = new ArrayList<>(); | ||
217 | return symbolicParameters; | ||
218 | } | ||
219 | |||
220 | /** | ||
221 | * Sets the exported parameter constraints of the body, if this instance is mutable. | ||
222 | * @param symbolicParameters the new value | ||
223 | */ | ||
224 | public void setSymbolicParameters(List<ExportedParameter> symbolicParameters) { | ||
225 | checkMutability(); | ||
226 | this.symbolicParameters = new ArrayList<>(symbolicParameters); | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * Sets a specific status for the body. If set, the parent PQuery status will not be checked; if set to null, its corresponding PQuery | ||
231 | * status is checked for mutability. | ||
232 | * | ||
233 | * @param status | ||
234 | * the status to set | ||
235 | */ | ||
236 | public void setStatus(PQueryStatus status) { | ||
237 | this.status = status; | ||
238 | } | ||
239 | |||
240 | public boolean isMutable() { | ||
241 | if (status == null) { | ||
242 | return query.isMutable(); | ||
243 | } else { | ||
244 | return status.equals(PQueryStatus.UNINITIALIZED); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | void checkMutability() { | ||
249 | if (status == null) { | ||
250 | query.checkMutability(); | ||
251 | } else { | ||
252 | Preconditions.checkState(status.equals(PQueryStatus.UNINITIALIZED), "Initialized queries are not mutable"); | ||
253 | } | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * Returns the disjunction the body is contained with. This disjunction may either be the | ||
258 | * {@link PQuery#getDisjunctBodies() canonical disjunction of the corresponding query} or something equivalent. | ||
259 | * | ||
260 | * @return the container disjunction of the body. Can be null if body is not in a disjunction yet. | ||
261 | */ | ||
262 | public PDisjunction getContainerDisjunction() { | ||
263 | return containerDisjunction; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * @param containerDisjunction the containerDisjunction to set | ||
268 | */ | ||
269 | public void setContainerDisjunction(PDisjunction containerDisjunction) { | ||
270 | Preconditions.checkArgument(query.equals(containerDisjunction.getQuery()), "Disjunction of pattern %s incompatible with body %s", containerDisjunction.getQuery().getFullyQualifiedName(), query.getFullyQualifiedName()); | ||
271 | Preconditions.checkState(this.containerDisjunction == null, "Disjunction is already set."); | ||
272 | this.containerDisjunction = containerDisjunction; | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * All unary input keys directly prescribed by constraints, grouped by variable. | ||
277 | * <p> to supertype inference or subsumption applied at this point. | ||
278 | */ | ||
279 | public Map<PVariable, Set<TypeJudgement>> getAllUnaryTypeRestrictions(IQueryMetaContext context) { | ||
280 | Map<PVariable, Set<TypeJudgement>> currentRestrictions = allUnaryTypeRestrictions.get(context); | ||
281 | if (currentRestrictions == null) { | ||
282 | currentRestrictions = TypeHelper.inferUnaryTypes(getConstraints(), context); | ||
283 | allUnaryTypeRestrictions.put(context, currentRestrictions); | ||
284 | } | ||
285 | return currentRestrictions; | ||
286 | } | ||
287 | private WeakHashMap<IQueryMetaContext, Map<PVariable, Set<TypeJudgement>>> allUnaryTypeRestrictions = new WeakHashMap<IQueryMetaContext, Map<PVariable,Set<TypeJudgement>>>(); | ||
288 | |||
289 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java new file mode 100644 index 00000000..ae2c4632 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java | |||
@@ -0,0 +1,70 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Comparator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
18 | |||
19 | /** | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public interface PConstraint extends PTraceable { | ||
24 | |||
25 | /** | ||
26 | * @since 2.1 | ||
27 | * @return the query body this constraint belongs to | ||
28 | */ | ||
29 | public PBody getBody(); | ||
30 | |||
31 | /** | ||
32 | * All variables affected by this constraint. | ||
33 | */ | ||
34 | public Set<PVariable> getAffectedVariables(); | ||
35 | |||
36 | /** | ||
37 | * The set of variables whose potential values can be enumerated (once all non-deduced variables have known values). | ||
38 | */ | ||
39 | public Set<PVariable> getDeducedVariables(); | ||
40 | |||
41 | /** | ||
42 | * A (preferably minimal) cover of known functional dependencies between variables. | ||
43 | * @noreference Use {@link QueryAnalyzer} instead to properly handle dependencies of pattern calls. | ||
44 | * @return non-trivial functional dependencies in the form of {variables} --> {variables}, where dependencies with the same lhs are unified. | ||
45 | */ | ||
46 | public Map<Set<PVariable>,Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context); | ||
47 | |||
48 | public void replaceVariable(PVariable obsolete, PVariable replacement); | ||
49 | |||
50 | public void delete(); | ||
51 | |||
52 | public void checkSanity(); | ||
53 | |||
54 | /** | ||
55 | * Returns an integer ID that is guaranteed to increase strictly monotonously for constraints within a pBody. | ||
56 | */ | ||
57 | public abstract int getMonotonousID(); | ||
58 | |||
59 | |||
60 | /** | ||
61 | * A comparator that orders constraints by their {@link #getMonotonousID() monotonous identifiers}. Should only used | ||
62 | * for tiebreaking in other comparators. | ||
63 | * | ||
64 | * @since 2.0 | ||
65 | */ | ||
66 | public static final Comparator<PConstraint> COMPARE_BY_MONOTONOUS_ID = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID(); | ||
67 | |||
68 | |||
69 | |||
70 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java new file mode 100644 index 00000000..f0241a9c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java | |||
@@ -0,0 +1,16 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Dénes Harmath, IncQueryLabs Ltd. | ||
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; | ||
10 | |||
11 | /** | ||
12 | * Marker interface for PSystem elements that can be traced. | ||
13 | * @since 1.6 | ||
14 | */ | ||
15 | public interface PTraceable { | ||
16 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java new file mode 100644 index 00000000..b6ea4861 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java | |||
@@ -0,0 +1,203 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * | ||
18 | */ | ||
19 | public class PVariable { | ||
20 | private PBody pBody; | ||
21 | /** | ||
22 | * The name of the pattern variable. This is the unique key of the pattern node. | ||
23 | */ | ||
24 | private String name; | ||
25 | /** | ||
26 | * virtual pVariables are nodes that do not correspond to actual pattern variables; they represent constants or Term | ||
27 | * substitutes | ||
28 | */ | ||
29 | private boolean virtual; | ||
30 | |||
31 | /** | ||
32 | * Set of constraints that mention this variable | ||
33 | */ | ||
34 | private Set<PConstraint> referringConstraints; | ||
35 | |||
36 | /** | ||
37 | * Determines whether there are any constraints that can deduce this variable | ||
38 | */ | ||
39 | private Boolean deducable; | ||
40 | |||
41 | /** | ||
42 | * Another PVariable this variable has been unified into. Please use the other variable instead of this. Null iff | ||
43 | * this is still a first-class variable. | ||
44 | */ | ||
45 | private PVariable unifiedInto; | ||
46 | |||
47 | PVariable(PBody pBody, String name) { | ||
48 | this(pBody, name, false); | ||
49 | } | ||
50 | |||
51 | PVariable(PBody pBody, String name, boolean virtual) { | ||
52 | super(); | ||
53 | this.pBody = pBody; | ||
54 | this.name = name; | ||
55 | this.virtual = virtual; | ||
56 | // this.exportedParameter = false; | ||
57 | this.referringConstraints = new HashSet<PConstraint>(); | ||
58 | this.unifiedInto = null; | ||
59 | this.deducable = false; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Replaces this variable with a given other, resulting in their unification. This variable will no longer be | ||
64 | * unique. | ||
65 | * | ||
66 | * @param replacement | ||
67 | */ | ||
68 | public void unifyInto(PVariable replacement) { | ||
69 | pBody.checkMutability(); | ||
70 | replacementCheck(); | ||
71 | replacement = replacement.getUnifiedIntoRoot(); | ||
72 | |||
73 | if (this.equals(replacement)) | ||
74 | return; | ||
75 | |||
76 | if (!this.isVirtual() && replacement.isVirtual()) { | ||
77 | replacement.unifyInto(this); | ||
78 | } else { | ||
79 | // replacement.referringConstraints.addAll(this.referringConstraints); | ||
80 | // replacement.exportedParameter |= this.exportedParameter; | ||
81 | replacement.virtual &= this.virtual; | ||
82 | if (replacement.deducable != null && this.deducable != null) | ||
83 | replacement.deducable |= this.deducable; | ||
84 | else | ||
85 | replacement.deducable = null; | ||
86 | Set<PConstraint> snapshotConstraints = // avoid ConcurrentModificationX | ||
87 | new HashSet<PConstraint>(this.referringConstraints); | ||
88 | for (PConstraint constraint : snapshotConstraints) { | ||
89 | constraint.replaceVariable(this, replacement); | ||
90 | } | ||
91 | // replacementCheck() will fail from this point | ||
92 | this.unifiedInto = replacement; | ||
93 | pBody.noLongerUnique(this); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Determines whether there are any constraints that can deduce this variable | ||
99 | */ | ||
100 | public boolean isDeducable() { | ||
101 | replacementCheck(); | ||
102 | if (deducable == null) { | ||
103 | deducable = false; | ||
104 | for (PConstraint pConstraint : getReferringConstraints()) { | ||
105 | if (pConstraint.getDeducedVariables().contains(this)) { | ||
106 | deducable = true; | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | return deducable; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Register that this variable is referred by the given constraint. | ||
116 | * | ||
117 | * @param constraint | ||
118 | */ | ||
119 | public void refer(PConstraint constraint) { | ||
120 | pBody.checkMutability(); | ||
121 | replacementCheck(); | ||
122 | deducable = null; | ||
123 | referringConstraints.add(constraint); | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Register that this variable is no longer referred by the given constraint. | ||
128 | * | ||
129 | * @param constraint | ||
130 | */ | ||
131 | public void unrefer(PConstraint constraint) { | ||
132 | pBody.checkMutability(); | ||
133 | replacementCheck(); | ||
134 | deducable = null; | ||
135 | referringConstraints.remove(constraint); | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * @return the name of the pattern variable. This is the unique key of the pattern node. | ||
140 | */ | ||
141 | public String getName() { | ||
142 | replacementCheck(); | ||
143 | return name; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * @return the virtual | ||
148 | */ | ||
149 | public boolean isVirtual() { | ||
150 | replacementCheck(); | ||
151 | return virtual; | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * @return the referringConstraints | ||
156 | */ | ||
157 | public Set<PConstraint> getReferringConstraints() { | ||
158 | replacementCheck(); | ||
159 | return referringConstraints; | ||
160 | } | ||
161 | |||
162 | @SuppressWarnings("unchecked") | ||
163 | public <ConstraintType> Set<ConstraintType> getReferringConstraintsOfType(Class<ConstraintType> constraintClass) { | ||
164 | replacementCheck(); | ||
165 | Set<ConstraintType> result = new HashSet<ConstraintType>(); | ||
166 | for (PConstraint pConstraint : referringConstraints) { | ||
167 | if (constraintClass.isInstance(pConstraint)) | ||
168 | result.add((ConstraintType) pConstraint); | ||
169 | } | ||
170 | return result; | ||
171 | } | ||
172 | |||
173 | @Override | ||
174 | public String toString() { | ||
175 | // replacementCheck(); | ||
176 | return name;// + ":PatternNode"; | ||
177 | } | ||
178 | |||
179 | public PVariable getDirectUnifiedInto() { | ||
180 | return unifiedInto; | ||
181 | } | ||
182 | |||
183 | public PVariable getUnifiedIntoRoot() { | ||
184 | PVariable nextUnified = unifiedInto; | ||
185 | PVariable oldUnifiedInto = this; | ||
186 | while (nextUnified != null) { | ||
187 | oldUnifiedInto = nextUnified; | ||
188 | nextUnified = oldUnifiedInto.getDirectUnifiedInto(); | ||
189 | } | ||
190 | return oldUnifiedInto; // unifiedInto; | ||
191 | } | ||
192 | |||
193 | public boolean isUnique() { | ||
194 | return unifiedInto == null; | ||
195 | } | ||
196 | |||
197 | private void replacementCheck() { | ||
198 | if (unifiedInto != null) | ||
199 | throw new IllegalStateException("Illegal usage of variable " + name + " which has been replaced with " | ||
200 | + unifiedInto.name); | ||
201 | } | ||
202 | |||
203 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java new file mode 100644 index 00000000..4447b225 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java | |||
@@ -0,0 +1,153 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.stream.Collectors; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * A judgement that means that the given tuple of variables will represent a tuple of values that is a member of the extensional relation identified by the given input key. | ||
28 | * @author Bergmann Gabor | ||
29 | * | ||
30 | */ | ||
31 | public class TypeJudgement { | ||
32 | |||
33 | private IInputKey inputKey; | ||
34 | private Tuple variablesTuple; | ||
35 | /** | ||
36 | * @param inputKey | ||
37 | * @param variablesTuple | ||
38 | */ | ||
39 | public TypeJudgement(IInputKey inputKey, Tuple variablesTuple) { | ||
40 | super(); | ||
41 | this.inputKey = inputKey; | ||
42 | this.variablesTuple = variablesTuple; | ||
43 | } | ||
44 | public IInputKey getInputKey() { | ||
45 | return inputKey; | ||
46 | } | ||
47 | public Tuple getVariablesTuple() { | ||
48 | return variablesTuple; | ||
49 | } | ||
50 | @Override | ||
51 | public int hashCode() { | ||
52 | final int prime = 31; | ||
53 | int result = 1; | ||
54 | result = prime * result | ||
55 | + ((inputKey == null) ? 0 : inputKey.hashCode()); | ||
56 | result = prime * result | ||
57 | + ((variablesTuple == null) ? 0 : variablesTuple.hashCode()); | ||
58 | return result; | ||
59 | } | ||
60 | @Override | ||
61 | public boolean equals(Object obj) { | ||
62 | if (this == obj) | ||
63 | return true; | ||
64 | if (obj == null) | ||
65 | return false; | ||
66 | if (!(obj instanceof TypeJudgement)) | ||
67 | return false; | ||
68 | TypeJudgement other = (TypeJudgement) obj; | ||
69 | if (inputKey == null) { | ||
70 | if (other.inputKey != null) | ||
71 | return false; | ||
72 | } else if (!inputKey.equals(other.inputKey)) | ||
73 | return false; | ||
74 | if (variablesTuple == null) { | ||
75 | if (other.variablesTuple != null) | ||
76 | return false; | ||
77 | } else if (!variablesTuple.equals(other.variablesTuple)) | ||
78 | return false; | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | public Set<TypeJudgement> getDirectlyImpliedJudgements(IQueryMetaContext context) { | ||
83 | Set<TypeJudgement> results = new HashSet<TypeJudgement>(); | ||
84 | results.add(this); | ||
85 | |||
86 | Collection<InputKeyImplication> implications = context.getImplications(this.inputKey); | ||
87 | for (InputKeyImplication inputKeyImplication : implications) { | ||
88 | results.add( | ||
89 | transcribeImplication(inputKeyImplication) | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | return results; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * @since 1.6 | ||
98 | */ | ||
99 | public Set<TypeJudgement> getWeakenedAlternativeJudgements(IQueryMetaContext context) { | ||
100 | Set<TypeJudgement> results = new HashSet<TypeJudgement>(); | ||
101 | |||
102 | Collection<InputKeyImplication> implications = context.getWeakenedAlternatives(this.inputKey); | ||
103 | for (InputKeyImplication inputKeyImplication : implications) { | ||
104 | results.add( | ||
105 | transcribeImplication(inputKeyImplication) | ||
106 | ); | ||
107 | } | ||
108 | |||
109 | return results; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | public Map<TypeJudgement, Set<TypeJudgement>> getConditionalImpliedJudgements(IQueryMetaContext context) { | ||
116 | return context.getConditionalImplications(this.inputKey).entrySet().stream().collect(Collectors.toMap( | ||
117 | entry -> transcribeImplication(entry.getKey()), | ||
118 | entry -> entry.getValue().stream().map(this::transcribeImplication).collect(Collectors.toSet()))); | ||
119 | } | ||
120 | |||
121 | |||
122 | |||
123 | private TypeJudgement transcribeImplication(InputKeyImplication inputKeyImplication) { | ||
124 | return new TypeJudgement( | ||
125 | inputKeyImplication.getImpliedKey(), | ||
126 | transcribeVariablesToTuple(inputKeyImplication.getImpliedIndices()) | ||
127 | ); | ||
128 | } | ||
129 | private Tuple transcribeVariablesToTuple(List<Integer> indices) { | ||
130 | Object[] elements = new Object[indices.size()]; | ||
131 | for (int i = 0; i < indices.size(); ++i) | ||
132 | elements[i] = variablesTuple.get(indices.get(i)); | ||
133 | return Tuples.flatTupleOf(elements); | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public String toString() { | ||
138 | return "TypeJudgement:" + inputKey.getPrettyPrintableName() + "@" + variablesTuple.toString(); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * Creates this judgement as a direct type constraint in the given PBody under construction. | ||
143 | * <p> pre: the variables tuple must be formed of variables of that PBody. | ||
144 | * @since 1.6 | ||
145 | */ | ||
146 | public PConstraint createConstraintFor(PBody pBody) { | ||
147 | if (inputKey.isEnumerable()) { | ||
148 | return new TypeConstraint(pBody, variablesTuple, inputKey); | ||
149 | } else { | ||
150 | return new TypeFilterConstraint(pBody, variablesTuple, inputKey); | ||
151 | } | ||
152 | } | ||
153 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java new file mode 100644 index 00000000..8ea6bb93 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java | |||
@@ -0,0 +1,40 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
16 | |||
17 | /** | ||
18 | * A kind of deferred constraint that can only be checked when a set of deferring variables are all present in a plan. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public abstract class VariableDeferredPConstraint extends DeferredPConstraint { | ||
24 | |||
25 | public VariableDeferredPConstraint(PBody pBody, | ||
26 | Set<PVariable> affectedVariables) { | ||
27 | super(pBody, affectedVariables); | ||
28 | } | ||
29 | |||
30 | public abstract Set<PVariable> getDeferringVariables(); | ||
31 | |||
32 | /** | ||
33 | * Refine further if needed | ||
34 | */ | ||
35 | @Override | ||
36 | public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { | ||
37 | return plan.getVisibleVariables().containsAll(getDeferringVariables()); | ||
38 | } | ||
39 | |||
40 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java new file mode 100644 index 00000000..63a37bbe --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregations; | ||
10 | |||
11 | /** | ||
12 | * | ||
13 | * An aggregation operator that does not store interim results beyond the final aggregated value. | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.4 | ||
16 | */ | ||
17 | public abstract class AbstractMemorylessAggregationOperator<Domain, AggregateResult> | ||
18 | implements IMultisetAggregationOperator<Domain, AggregateResult, AggregateResult> | ||
19 | { | ||
20 | |||
21 | @Override | ||
22 | public AggregateResult getAggregate(AggregateResult result) { | ||
23 | return result; | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public AggregateResult clone(AggregateResult original) { | ||
28 | return original; | ||
29 | } | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java new file mode 100644 index 00000000..4cc40a2b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.aggregations; | ||
10 | |||
11 | import java.lang.annotation.ElementType; | ||
12 | import java.lang.annotation.Inherited; | ||
13 | import java.lang.annotation.Retention; | ||
14 | import java.lang.annotation.RetentionPolicy; | ||
15 | import java.lang.annotation.Target; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.aggregators.count; | ||
18 | |||
19 | /** | ||
20 | * The aggregator type annotation describes the type constraints for the selected aggregator. In version 1.4, two kinds of | ||
21 | * aggregators are supported: | ||
22 | * | ||
23 | * <ol> | ||
24 | * <li>An aggregator that does not consider any parameter value from the call ({@link count}), just calculates the | ||
25 | * number of matches. This is represented by a single {@link Void} and a single corresponding return type.</li> | ||
26 | * <li>An aggregator that considers a single parameter from the call, and executes some aggregate operations over it. | ||
27 | * Such an aggregate operation can be defined over multiple types, where each possible parameter type has a corresponding return type declared.</li> | ||
28 | * </ol> | ||
29 | * | ||
30 | * <strong>Important!</strong> The parameterTypes and returnTypes arrays must have | ||
31 | * <ul> | ||
32 | * <li>The same number of classes defined each.</li> | ||
33 | * <li>Items are corresponded by index.</li> | ||
34 | * <li>Items should represent data types</li> | ||
35 | * </ul> | ||
36 | * | ||
37 | * @author Zoltan Ujhelyi | ||
38 | * @since 1.4 | ||
39 | * | ||
40 | */ | ||
41 | @Target({ ElementType.TYPE }) | ||
42 | @Retention(RetentionPolicy.RUNTIME) | ||
43 | @Inherited | ||
44 | public @interface AggregatorType { | ||
45 | |||
46 | Class<?>[] parameterTypes(); | ||
47 | |||
48 | Class<?>[] returnTypes(); | ||
49 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java new file mode 100644 index 00000000..e6972544 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregations; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; | ||
13 | |||
14 | /** | ||
15 | * Augments an aggregator operator with type bindings for the type of values being aggregated and the aggregate result. | ||
16 | * <p> In case of <em>count</em>, the operator should be null. | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.4 | ||
19 | */ | ||
20 | public class BoundAggregator { | ||
21 | private final IMultisetAggregationOperator<?, ?, ?> operator; | ||
22 | private final Class<?> domainType; | ||
23 | private final Class<?> aggregateResultType; | ||
24 | |||
25 | public BoundAggregator(IMultisetAggregationOperator<?, ?, ?> operator, | ||
26 | Class<?> domainType, | ||
27 | Class<?> aggregateResultType) { | ||
28 | super(); | ||
29 | this.operator = operator; | ||
30 | this.domainType = domainType; | ||
31 | this.aggregateResultType = aggregateResultType; | ||
32 | } | ||
33 | |||
34 | public IMultisetAggregationOperator<?, ?, ?> getOperator() { | ||
35 | return operator; | ||
36 | } | ||
37 | |||
38 | public Class<?> getDomainType() { | ||
39 | return domainType; | ||
40 | } | ||
41 | |||
42 | public Class<?> getAggregateResultType() { | ||
43 | return aggregateResultType; | ||
44 | } | ||
45 | |||
46 | public IInputKey getDomainTypeAsInputKey() { | ||
47 | return toJavaInputKey(domainType); | ||
48 | } | ||
49 | |||
50 | public IInputKey getAggregateResultTypeAsInputKey() { | ||
51 | return toJavaInputKey(aggregateResultType); | ||
52 | } | ||
53 | |||
54 | private static IInputKey toJavaInputKey(Class<?> type) { | ||
55 | if (type==null) { | ||
56 | return null; | ||
57 | } else { | ||
58 | return new JavaTransitiveInstancesKey(type); | ||
59 | } | ||
60 | } | ||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java new file mode 100644 index 00000000..c970bd6a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java | |||
@@ -0,0 +1,40 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, Tamas Szabo, 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.aggregations; | ||
10 | |||
11 | /** | ||
12 | * | ||
13 | * Describes an aggregation operator keyword, potentially with type polymorphism. The actual runtime | ||
14 | * {@link IMultisetAggregationOperator} that implements the aggregation logic may depend on the type context. | ||
15 | * | ||
16 | * <p> | ||
17 | * Implementors are suggested to use lower-case classnames (as it will end up in the language) and are required use the | ||
18 | * annotation {@link AggregatorType} to indicate type inference rules. | ||
19 | * | ||
20 | * <p> | ||
21 | * <strong>Important!</strong> Implemented aggregators must be (1) deterministic (2) pure and (3)support incremental | ||
22 | * value updates in the internal operation. | ||
23 | * | ||
24 | * @author Zoltan Ujhelyi | ||
25 | * @since 1.4 | ||
26 | */ | ||
27 | |||
28 | public interface IAggregatorFactory { | ||
29 | |||
30 | /** | ||
31 | * Given type parameters selected from {@link AggregatorType} annotations, returns a run-time aggregator operator | ||
32 | * that is bound to the actual types. | ||
33 | * | ||
34 | * @param domainClass | ||
35 | * Java type of the values that are being aggregated | ||
36 | * @return the actual run-time aggregator logic, with type bindings | ||
37 | */ | ||
38 | public BoundAggregator getAggregatorLogic(Class<?> domainClass); | ||
39 | |||
40 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java new file mode 100644 index 00000000..3bc22274 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java | |||
@@ -0,0 +1,106 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.aggregations; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.stream.Stream; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.aggregators.ExtremumOperator; | ||
15 | |||
16 | /** | ||
17 | * A single column aggregator is used to incrementally compute the aggregate of a multiset of values according to an aggregator operator. | ||
18 | * | ||
19 | * <p> The operator provides two methods of computation: <ul> | ||
20 | * <li>Stateless aggregation of an explicit multiset, provided by {@link #aggregateStatelessly(Collection)}.</li> | ||
21 | * <li>Incremental aggregation, provided by {@link #createNeutral()}, {@link #update(Object, Object, boolean)}, {@link #isNeutral(Object)}, {@link #getAggregate(Object)}. | ||
22 | * </ul> | ||
23 | * | ||
24 | * <p> In case of incremental computation, the aggregable multiset is conceptual; it is not represented by an explicit Collection<Domain> object, but its update operations are tracked. | ||
25 | * | ||
26 | * <p> In case of incremental computation, internal results, potentially distinct from the final aggregate result, may be stored in a helper data structure called <b>accumulator</b>. | ||
27 | * The goal of this distinction is that the final result may not be sufficient for incremental updates (see e.g. {@link ExtremumOperator}). | ||
28 | * | ||
29 | * @author Gabor Bergmann | ||
30 | * | ||
31 | * @param <Domain> the type of elements to be aggregated. | ||
32 | * @param <Accumulator> the type used to store the interim results of the aggregate computation, | ||
33 | * that may be incrementally refreshed upon updates to the multiset, and that can easily yield the final result. | ||
34 | * @param <AggregateResult> the type of the final result of the aggregation to be output. | ||
35 | * | ||
36 | * @since 1.4 | ||
37 | */ | ||
38 | public interface IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> { | ||
39 | |||
40 | /** | ||
41 | * A textual description of the operator. | ||
42 | */ | ||
43 | String getShortDescription(); | ||
44 | |||
45 | /** | ||
46 | * A name or identifier of the operator. | ||
47 | */ | ||
48 | String getName(); | ||
49 | |||
50 | /** | ||
51 | * @return the neutral element, i.e. the interim result of aggregating an empty multiset. | ||
52 | */ | ||
53 | Accumulator createNeutral(); | ||
54 | |||
55 | /** | ||
56 | * @return true if the interim result is equivalent to the neutral element, as if there are no values in the multiset. | ||
57 | * Must return true if the multiset is empty. | ||
58 | */ | ||
59 | boolean isNeutral(Accumulator result); | ||
60 | |||
61 | /** | ||
62 | * @return an updated intermediate result, | ||
63 | * changed to reflect that a given object was added to / removed from the multiset | ||
64 | * (as indicated by the parameter isInsertion) | ||
65 | */ | ||
66 | Accumulator update(Accumulator oldResult, Domain updateValue, boolean isInsertion); | ||
67 | |||
68 | /** | ||
69 | * @return the aggregate result obtained from the given intermediate result. | ||
70 | * May be null to indicate that the current multiset cannot be aggregated (e.g. 0 elements have no minimum). | ||
71 | */ | ||
72 | AggregateResult getAggregate(Accumulator result); | ||
73 | |||
74 | /** | ||
75 | * Calculates the aggregate results from a given stream of values; all values are considered as inserted | ||
76 | * @return the aggregate result, or null if no result can be calculated (e.g. because of an empty stream) | ||
77 | * @since 2.0 | ||
78 | */ | ||
79 | AggregateResult aggregateStream(Stream<Domain> stream); | ||
80 | |||
81 | /** | ||
82 | * Clones the given accumulator (with all its internal contents). | ||
83 | */ | ||
84 | default Accumulator clone(Accumulator original) { | ||
85 | throw new UnsupportedOperationException(); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * Combines the given aggregate result and accumulator into a single aggregate result. | ||
90 | */ | ||
91 | default AggregateResult combine(AggregateResult left, Accumulator right) { | ||
92 | throw new UnsupportedOperationException(); | ||
93 | } | ||
94 | |||
95 | default boolean contains(Domain value, Accumulator accumulator) { | ||
96 | throw new UnsupportedOperationException(); | ||
97 | } | ||
98 | |||
99 | /** | ||
100 | * Pretty prints the contents of the given accumulator. | ||
101 | */ | ||
102 | default String prettyPrint(final Accumulator accumulator) { | ||
103 | return accumulator.toString(); | ||
104 | } | ||
105 | |||
106 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java new file mode 100644 index 00000000..e3f28cff --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java | |||
@@ -0,0 +1,194 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.analysis; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.ParameterReference; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
30 | |||
31 | /** | ||
32 | * Object responsible for computing and caching static query analysis results. | ||
33 | * <p> Any client can instantiate this to statically analyze queries. | ||
34 | * Query backends should share an instance obtained via {@link IQueryBackendContext} to save resources. | ||
35 | * <p> Precondition: all involved queries must be initialized. | ||
36 | * @noinstantiate Considered unstable API; subject to change in future versions. | ||
37 | * Either use the analyzer provided by {@link IQueryBackendContext}, or anticipate | ||
38 | * potential future breakage when instantiating your own analyzer. | ||
39 | * @author Gabor Bergmann | ||
40 | * @since 1.5 | ||
41 | */ | ||
42 | public final class QueryAnalyzer { | ||
43 | |||
44 | private IQueryMetaContext metaContext; | ||
45 | |||
46 | public QueryAnalyzer(IQueryMetaContext metaContext) { | ||
47 | this.metaContext = metaContext; | ||
48 | } | ||
49 | |||
50 | // Functional dependencies | ||
51 | |||
52 | /** | ||
53 | * Maps query and strictness to functional dependencies | ||
54 | */ | ||
55 | private Map<PQuery, Map<Set<Integer>, Set<Integer>>> strictFunctionalDependencyGuarantees = | ||
56 | new HashMap<>(); | ||
57 | private Map<PQuery, Map<Set<Integer>, Set<Integer>>> softFunctionalDependencyGuarantees = | ||
58 | new HashMap<>(); | ||
59 | |||
60 | /** | ||
61 | * Functional dependency information, expressed on query parameters, that the match set of the query is guaranteed to respect. | ||
62 | * <p> The type dependencies shall be expressed on the <i>parameter index</i> integers, NOT the {@link PParameter} object. | ||
63 | * @return a non-null map of functional dependencies on parameters that can be processed by {@link FunctionalDependencyHelper} | ||
64 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
65 | * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
66 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
67 | * @since 1.5 | ||
68 | */ | ||
69 | public Map<Set<Integer>, Set<Integer>> getProjectedFunctionalDependencies(PQuery query, boolean strict) { | ||
70 | Map<PQuery, Map<Set<Integer>, Set<Integer>>> guaranteeStore = strict ? strictFunctionalDependencyGuarantees : softFunctionalDependencyGuarantees; | ||
71 | Map<Set<Integer>, Set<Integer>> dependencies = guaranteeStore.get(query); | ||
72 | // Why not computeIfAbsent? See Bug 532507 | ||
73 | // Invoked method #computeFunctionalDependencies may trigger functional dependency computation for called queries; | ||
74 | // and may thus recurs back into #getProjectedFunctionalDependencies, causing a ConcurrentModificationException | ||
75 | // if the called query has not been previously analyzed. | ||
76 | // | ||
77 | // Note: if patterns are recursive, the empty accumulator will be found in the store | ||
78 | // (this yields a safe lower estimate and guarantees termination for #getProjectedFunctionalDependencies) | ||
79 | // But this case probably will not occur due to recursive queries having a disjunction at some point, | ||
80 | // and thus ignored by #computeFunctionalDependencies | ||
81 | if (dependencies == null) { | ||
82 | dependencies = new HashMap<>(); // accumulator | ||
83 | guaranteeStore.put(query, dependencies); | ||
84 | computeFunctionalDependencies(dependencies, query, strict); | ||
85 | } | ||
86 | return dependencies; | ||
87 | } | ||
88 | |||
89 | private void computeFunctionalDependencies(Map<Set<Integer>, Set<Integer>> accumulator, PQuery query, boolean strict) { | ||
90 | Set<PBody> bodies = query.getDisjunctBodies().getBodies(); | ||
91 | if (bodies.size() == 1) { // no support for recursion or disjunction | ||
92 | |||
93 | PBody body = bodies.iterator().next(); | ||
94 | |||
95 | // collect parameter variables | ||
96 | Map<PVariable, Integer> parameters = body.getSymbolicParameters().stream() | ||
97 | .collect(Collectors.toMap(ExportedParameter::getParameterVariable, | ||
98 | param -> query.getParameters().indexOf(param.getPatternParameter()))); | ||
99 | |||
100 | // collect all internal dependencies | ||
101 | Map<Set<PVariable>, Set<PVariable>> internalDependencies = | ||
102 | getFunctionalDependencies(body.getConstraints(), strict); | ||
103 | |||
104 | // project onto parameter variables | ||
105 | Map<Set<PVariable>, Set<PVariable>> projectedDeps = | ||
106 | FunctionalDependencyHelper.projectDependencies(internalDependencies, parameters.keySet()); | ||
107 | |||
108 | // translate into indices | ||
109 | for (Entry<Set<PVariable>, Set<PVariable>> entry : projectedDeps.entrySet()) { | ||
110 | Set<Integer> left = new HashSet<Integer>(); | ||
111 | Set<Integer> right = new HashSet<Integer>(); | ||
112 | for (PVariable pVariable : entry.getKey()) { | ||
113 | left.add(parameters.get(pVariable)); | ||
114 | } | ||
115 | for (PVariable pVariable : entry.getValue()) { | ||
116 | right.add(parameters.get(pVariable)); | ||
117 | } | ||
118 | accumulator.put(left, right); | ||
119 | } | ||
120 | |||
121 | } else { | ||
122 | // Disjunctive case, no dependencies are inferred | ||
123 | // TODO: we can still salvage the intersection of dependencies IF | ||
124 | // - all bodies have disjoint match sets | ||
125 | // - and we avoid recursion | ||
126 | } | ||
127 | |||
128 | // add annotation-based soft dependencies (regardless of number of bodies) | ||
129 | if (!strict) { | ||
130 | outer: | ||
131 | for (PAnnotation annotation : query.getAnnotationsByName("FunctionalDependency")) { | ||
132 | Set<Integer> lefts = new HashSet<Integer>(); | ||
133 | Set<Integer> rights = new HashSet<Integer>(); | ||
134 | |||
135 | for (Object object : annotation.getAllValues("forEach")) { | ||
136 | ParameterReference parameter = (ParameterReference) object; | ||
137 | Integer position = query.getPositionOfParameter(parameter.getName()); | ||
138 | if (position == null) continue outer; | ||
139 | lefts.add(position); | ||
140 | } | ||
141 | for (Object object : annotation.getAllValues("unique")) { | ||
142 | ParameterReference parameter = (ParameterReference) object; | ||
143 | Integer position = query.getPositionOfParameter(parameter.getName()); | ||
144 | if (position == null) continue outer; | ||
145 | rights.add(position); | ||
146 | } | ||
147 | |||
148 | FunctionalDependencyHelper.includeDependency(accumulator, lefts, rights); | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * Functional dependency information, expressed on PVariables within a body, that the selected constraints imply. | ||
155 | * @return a non-null map of functional dependencies on PVariables that can be processed by {@link FunctionalDependencyHelper} | ||
156 | * @param constraints the set of constraints whose consequences will be analyzed | ||
157 | * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; | ||
158 | * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; | ||
159 | * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance | ||
160 | * @since 1.5 | ||
161 | */ | ||
162 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(Set<? extends PConstraint> constraints, boolean strict) { | ||
163 | Map<Set<PVariable>, Set<PVariable>> accumulator = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
164 | for (PConstraint pConstraint : constraints){ | ||
165 | if (pConstraint instanceof PositivePatternCall) { | ||
166 | // use query analysis results instead | ||
167 | PositivePatternCall call = (PositivePatternCall) pConstraint; | ||
168 | PQuery query = call.getSupplierKey(); | ||
169 | Map<Set<Integer>, Set<Integer>> paramDependencies = getProjectedFunctionalDependencies(query, strict); | ||
170 | for (Entry<Set<Integer>, Set<Integer>> entry : paramDependencies.entrySet()) { | ||
171 | Set<PVariable> lefts = new HashSet<PVariable>(); | ||
172 | Set<PVariable> rights = new HashSet<PVariable>(); | ||
173 | |||
174 | for (Integer index : entry.getKey()) { | ||
175 | lefts.add(call.getVariableInTuple(index)); | ||
176 | } | ||
177 | for (Integer index : entry.getValue()) { | ||
178 | rights.add(call.getVariableInTuple(index)); | ||
179 | } | ||
180 | |||
181 | FunctionalDependencyHelper.includeDependency(accumulator, | ||
182 | lefts, rights); | ||
183 | } | ||
184 | } else { | ||
185 | // delegate to PConstraint | ||
186 | FunctionalDependencyHelper.includeDependencies(accumulator, | ||
187 | pConstraint.getFunctionalDependencies(metaContext)); | ||
188 | } | ||
189 | } | ||
190 | return accumulator; | ||
191 | } | ||
192 | |||
193 | |||
194 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java new file mode 100644 index 00000000..c4fbe0e9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.annotations; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Optional; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.BiConsumer; | ||
15 | |||
16 | import org.eclipse.collections.api.multimap.MutableMultimap; | ||
17 | import org.eclipse.collections.impl.multimap.list.FastListMultimap; | ||
18 | |||
19 | /** | ||
20 | * A container describing query annotations | ||
21 | * @author Zoltan Ujhelyi | ||
22 | * | ||
23 | */ | ||
24 | public class PAnnotation { | ||
25 | |||
26 | private final String name; | ||
27 | private MutableMultimap<String, Object> attributes = FastListMultimap.newMultimap(); | ||
28 | |||
29 | public PAnnotation(String name) { | ||
30 | this.name = name; | ||
31 | |||
32 | } | ||
33 | |||
34 | /** | ||
35 | * Adds an attribute to the annotation | ||
36 | * @param attributeName | ||
37 | * @param value | ||
38 | */ | ||
39 | public void addAttribute(String attributeName, Object value) { | ||
40 | attributes.put(attributeName, value); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Return the name of the annotation | ||
45 | */ | ||
46 | public String getName() { | ||
47 | return name; | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * Returns the value of the first occurrence of an attribute | ||
52 | * @param attributeName | ||
53 | * @return the attribute value, or null, if attribute is not available | ||
54 | * @since 2.0 | ||
55 | */ | ||
56 | public Optional<Object> getFirstValue(String attributeName) { | ||
57 | return getAllValues(attributeName).stream().findFirst(); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Returns the value of the first occurrence of an attribute | ||
62 | * @param attributeName | ||
63 | * @return the attribute value, or null, if attribute is not available | ||
64 | * @since 2.0 | ||
65 | */ | ||
66 | public <T> Optional<T> getFirstValue(String attributeName, Class<T> clazz) { | ||
67 | return getAllValues(attributeName).stream().filter(clazz::isInstance).map(clazz::cast).findFirst(); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Returns all values of a selected attribute | ||
72 | * @param attributeName | ||
73 | * @return a non-null, but possibly empty list of attributes | ||
74 | */ | ||
75 | public List<Object> getAllValues(String attributeName) { | ||
76 | return attributes.get(attributeName).toList(); | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Executes a consumer over all attributes. A selected attribute name (key) can appear (and thus consumed) multiple times. | ||
81 | * @since 2.0 | ||
82 | */ | ||
83 | public void forEachValue(BiConsumer<String, Object> consumer) { | ||
84 | attributes.forEachKeyValue(consumer::accept); | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Returns a set of all attribute names used in this annotation | ||
89 | * @since 2.1 | ||
90 | */ | ||
91 | public Set<String> getAllAttributeNames() { | ||
92 | return attributes.keySet().toSet(); | ||
93 | } | ||
94 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java new file mode 100644 index 00000000..c67e9046 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java | |||
@@ -0,0 +1,30 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.annotations; | ||
10 | |||
11 | /** | ||
12 | * An annotation parameter referencing a query parameter by name. Does not check whether the parameter exists. | ||
13 | * | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * | ||
16 | */ | ||
17 | public class ParameterReference { | ||
18 | |||
19 | final String name; | ||
20 | |||
21 | public ParameterReference(String name) { | ||
22 | super(); | ||
23 | this.name = name; | ||
24 | } | ||
25 | |||
26 | public String getName() { | ||
27 | return name; | ||
28 | } | ||
29 | |||
30 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java new file mode 100644 index 00000000..56f86e89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java | |||
@@ -0,0 +1,98 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, 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.basicdeferred; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
25 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
26 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
27 | |||
28 | /** | ||
29 | * The PSystem representation of an aggregation. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * @since 1.4 | ||
33 | */ | ||
34 | public class AggregatorConstraint extends PatternCallBasedDeferred implements ITypeInfoProviderConstraint { | ||
35 | |||
36 | protected PVariable resultVariable; | ||
37 | private BoundAggregator aggregator; | ||
38 | protected int aggregatedColumn; | ||
39 | |||
40 | public AggregatorConstraint(BoundAggregator aggregator, PBody pBody, Tuple actualParametersTuple, PQuery query, | ||
41 | PVariable resultVariable, int aggregatedColumn) { | ||
42 | super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); | ||
43 | this.resultVariable = resultVariable; | ||
44 | this.aggregatedColumn = aggregatedColumn; | ||
45 | this.aggregator = aggregator; | ||
46 | } | ||
47 | |||
48 | public int getAggregatedColumn() { | ||
49 | return this.aggregatedColumn; | ||
50 | } | ||
51 | |||
52 | public BoundAggregator getAggregator() { | ||
53 | return this.aggregator; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public Set<PVariable> getDeducedVariables() { | ||
58 | return Collections.singleton(resultVariable); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
63 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
64 | result.put(getDeferringVariables(), getDeducedVariables()); | ||
65 | return result; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { | ||
70 | if (resultVariable.equals(obsolete)) | ||
71 | resultVariable = replacement; | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | protected Set<PVariable> getCandidateQuantifiedVariables() { | ||
76 | return actualParametersTuple.<PVariable> getDistinctElements(); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | protected String toStringRest() { | ||
81 | return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" | ||
82 | + resultVariable.toString(); | ||
83 | } | ||
84 | |||
85 | public PVariable getResultVariable() { | ||
86 | return resultVariable; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
91 | Set<TypeJudgement> result = new HashSet<TypeJudgement>(); | ||
92 | IInputKey aggregateResultType = aggregator.getAggregateResultTypeAsInputKey(); | ||
93 | if (aggregateResultType != null) { | ||
94 | result.add(new TypeJudgement(aggregateResultType, Tuples.staticArityFlatTupleOf(resultVariable))); | ||
95 | } | ||
96 | return result; | ||
97 | } | ||
98 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java new file mode 100644 index 00000000..7bc949a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java | |||
@@ -0,0 +1,99 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | */ | ||
28 | public abstract class BaseTypeSafeConstraint extends | ||
29 | VariableDeferredPConstraint { | ||
30 | |||
31 | protected Set<PVariable> inputVariables; | ||
32 | protected PVariable outputVariable; | ||
33 | |||
34 | public PVariable getOutputVariable() { | ||
35 | return outputVariable; | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * @param pBody | ||
40 | * @param inputVariables | ||
41 | * @param outputVariable null iff no output (check-only) | ||
42 | */ | ||
43 | public BaseTypeSafeConstraint(PBody pBody, | ||
44 | Set<PVariable> inputVariables, final PVariable outputVariable) { | ||
45 | super(pBody, | ||
46 | (outputVariable == null) ? | ||
47 | inputVariables : | ||
48 | Stream.concat(inputVariables.stream(), Stream.of(outputVariable)).collect(Collectors.toSet()) | ||
49 | ); | ||
50 | this.inputVariables = inputVariables; | ||
51 | this.outputVariable = outputVariable; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public Set<PVariable> getDeducedVariables() { | ||
56 | if (outputVariable == null) | ||
57 | return Collections.emptySet(); | ||
58 | else | ||
59 | return Collections.singleton(outputVariable); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Set<PVariable> getDeferringVariables() { | ||
64 | return inputVariables; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { | ||
69 | if (super.isReadyAt(plan, context)) { | ||
70 | return checkTypeSafety(plan, context) == null; | ||
71 | } | ||
72 | return false; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * Checks whether all type restrictions are already enforced on affected variables. | ||
77 | * | ||
78 | * @param plan | ||
79 | * @return a variable whose type safety is not enforced yet, or null if the plan is typesafe | ||
80 | */ | ||
81 | public PVariable checkTypeSafety(SubPlan plan, IQueryMetaContext context) { | ||
82 | Set<TypeJudgement> impliedJudgements = plan.getAllImpliedTypeJudgements(context); | ||
83 | |||
84 | for (PVariable pVariable : inputVariables) { | ||
85 | Set<TypeJudgement> allTypeRestrictionsForVariable = pBody.getAllUnaryTypeRestrictions(context).get(pVariable); | ||
86 | if (allTypeRestrictionsForVariable != null && !impliedJudgements.containsAll(allTypeRestrictionsForVariable)) | ||
87 | return pVariable; | ||
88 | } | ||
89 | return null; | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
94 | if (inputVariables.remove(obsolete)) | ||
95 | inputVariables.add(replacement); | ||
96 | if (outputVariable == obsolete) | ||
97 | outputVariable = replacement; | ||
98 | } | ||
99 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java new file mode 100644 index 00000000..b978b62c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java | |||
@@ -0,0 +1,96 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | */ | ||
28 | public class Equality extends DeferredPConstraint { | ||
29 | |||
30 | private PVariable who; | ||
31 | private PVariable withWhom; | ||
32 | |||
33 | public Equality(PBody pBody, PVariable who, PVariable withWhom) { | ||
34 | super(pBody, buildSet(who, withWhom)); | ||
35 | this.who = who; | ||
36 | this.withWhom = withWhom; | ||
37 | } | ||
38 | |||
39 | private static Set<PVariable> buildSet(PVariable who, PVariable withWhom) { | ||
40 | Set<PVariable> set = new HashSet<PVariable>(); | ||
41 | set.add(who); | ||
42 | set.add(withWhom); | ||
43 | return set; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * An equality is moot if it compares the a variable with itself. | ||
48 | * | ||
49 | * @return true, if the equality is moot | ||
50 | */ | ||
51 | public boolean isMoot() { | ||
52 | return who.equals(withWhom); | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
57 | if (obsolete.equals(who)) | ||
58 | who = replacement; | ||
59 | if (obsolete.equals(withWhom)) | ||
60 | withWhom = replacement; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | protected String toStringRest() { | ||
65 | return who.getName() + "=" + withWhom.getName(); | ||
66 | } | ||
67 | |||
68 | public PVariable getWho() { | ||
69 | return who; | ||
70 | } | ||
71 | |||
72 | public PVariable getWithWhom() { | ||
73 | return withWhom; | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public Set<PVariable> getDeducedVariables() { | ||
78 | return Collections.emptySet(); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
83 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
84 | result.put(Collections.singleton(who), Collections.singleton(withWhom)); | ||
85 | result.put(Collections.singleton(withWhom), Collections.singleton(who)); | ||
86 | return result; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { | ||
91 | return plan.getVisibleVariables().contains(who) && plan.getVisibleVariables().contains(withWhom); | ||
92 | // will be replaced by || if copierNode is available; | ||
93 | // until then, LayoutHelper.unifyVariablesAlongEqualities(PSystem<PatternDescription, StubHandle, Collector>) is | ||
94 | // recommended. | ||
95 | } | ||
96 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java new file mode 100644 index 00000000..80706792 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | |||
22 | /** | ||
23 | * @author Gabor Bergmann | ||
24 | * | ||
25 | */ | ||
26 | public class ExportedParameter extends VariableDeferredPConstraint { | ||
27 | PVariable parameterVariable; | ||
28 | final String parameterName; | ||
29 | final PParameter patternParameter; | ||
30 | |||
31 | /** | ||
32 | * @since 1.4 | ||
33 | */ | ||
34 | public ExportedParameter(PBody pBody, PVariable parameterVariable, PParameter patternParameter) { | ||
35 | super(pBody, Collections.singleton(parameterVariable)); | ||
36 | this.parameterVariable = parameterVariable; | ||
37 | this.patternParameter = patternParameter; | ||
38 | parameterName = patternParameter.getName(); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
43 | if (obsolete.equals(parameterVariable)) | ||
44 | parameterVariable = replacement; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | protected String toStringRest() { | ||
49 | Object varName = parameterVariable.getName(); | ||
50 | return parameterName.equals(varName) ? parameterName : parameterName + "(" + varName + ")"; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Set<PVariable> getDeducedVariables() { | ||
55 | return Collections.emptySet(); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * The name of the parameter; usually, it is expected that {@link #getParameterVariable()} is more useful, except | ||
60 | * maybe for debugging purposes. | ||
61 | * | ||
62 | * @return a non-null name of the parameter | ||
63 | */ | ||
64 | public String getParameterName() { | ||
65 | return parameterName; | ||
66 | } | ||
67 | |||
68 | public PVariable getParameterVariable() { | ||
69 | return parameterVariable; | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * @since 1.4 | ||
74 | */ | ||
75 | public PParameter getPatternParameter() { | ||
76 | if (patternParameter == null) { | ||
77 | PQuery query = pBody.getPattern(); | ||
78 | Integer index = query.getPositionOfParameter(parameterName); | ||
79 | if (index == null) { | ||
80 | throw new IllegalStateException(String.format("Pattern %s does not have a parameter named %s", | ||
81 | query.getFullyQualifiedName(), parameterName)); | ||
82 | } | ||
83 | return query.getParameters().get(index); | ||
84 | } else { | ||
85 | return patternParameter; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Set<PVariable> getDeferringVariables() { | ||
91 | return Collections.singleton(parameterVariable); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public void checkSanity() { | ||
96 | super.checkSanity(); | ||
97 | if (!parameterVariable.isDeducable()) { | ||
98 | String[] args = { parameterName }; | ||
99 | String msg = "Impossible to match pattern: " | ||
100 | + "exported pattern variable {1} can not be determined based on the pattern constraints. " | ||
101 | + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters."; | ||
102 | String shortMsg = "Could not deduce value of parameter"; | ||
103 | throw new QueryProcessingException(msg, args, shortMsg, null); | ||
104 | } | ||
105 | |||
106 | } | ||
107 | |||
108 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java new file mode 100644 index 00000000..06688c36 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java | |||
@@ -0,0 +1,80 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.basicdeferred; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.LinkedHashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | |||
23 | /** | ||
24 | * @author Zoltan Ujhelyi | ||
25 | * | ||
26 | */ | ||
27 | public class ExpressionEvaluation extends BaseTypeSafeConstraint { | ||
28 | |||
29 | private IExpressionEvaluator evaluator; | ||
30 | private boolean isUnwinding; | ||
31 | |||
32 | public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable) { | ||
33 | this(pBody, evaluator, outputVariable, false); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * @since 2.4 | ||
38 | */ | ||
39 | public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable, | ||
40 | boolean isUnwinding) { | ||
41 | super(pBody, getPVariablesOfExpression(pBody, evaluator), outputVariable); | ||
42 | this.evaluator = evaluator; | ||
43 | this.isUnwinding = isUnwinding; | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * @since 2.4 | ||
48 | */ | ||
49 | public boolean isUnwinding() { | ||
50 | return isUnwinding; | ||
51 | } | ||
52 | |||
53 | public IExpressionEvaluator getEvaluator() { | ||
54 | return evaluator; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | protected String toStringRest() { | ||
59 | return Tuples.flatTupleOf(new ArrayList<PVariable>(inputVariables).toArray()).toString() + "|=" | ||
60 | + evaluator.getShortDescription(); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
65 | if (outputVariable == null) | ||
66 | return Collections.emptyMap(); | ||
67 | else | ||
68 | return Collections.singletonMap(inputVariables, Collections.singleton(outputVariable)); | ||
69 | } | ||
70 | |||
71 | private static Set<PVariable> getPVariablesOfExpression(PBody pBody, IExpressionEvaluator evaluator) { | ||
72 | // use a linked set, so that the variables will come in the order of the parameters | ||
73 | Set<PVariable> result = new LinkedHashSet<PVariable>(); | ||
74 | for (String name : evaluator.getInputParameterNames()) { | ||
75 | PVariable variable = pBody.getOrCreateVariableByName(name); | ||
76 | result.add(variable); | ||
77 | } | ||
78 | return result; | ||
79 | } | ||
80 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java new file mode 100644 index 00000000..5cac33dc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java | |||
@@ -0,0 +1,151 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
20 | |||
21 | /** | ||
22 | * @author Gabor Bergmann | ||
23 | * | ||
24 | */ | ||
25 | public class Inequality extends VariableDeferredPConstraint { | ||
26 | |||
27 | private PVariable who; | ||
28 | private PVariable withWhom; | ||
29 | |||
30 | /** | ||
31 | * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is | ||
32 | * undeducible. | ||
33 | */ | ||
34 | private boolean weak; | ||
35 | |||
36 | public Inequality(PBody pBody, PVariable who, PVariable withWhom) { | ||
37 | this(pBody, who, withWhom, false); | ||
38 | } | ||
39 | |||
40 | public Inequality(PBody pBody, PVariable who, PVariable withWhom, | ||
41 | boolean weak) { | ||
42 | super(pBody, new HashSet<>(Arrays.asList(who, withWhom) )); | ||
43 | this.who = who; | ||
44 | this.withWhom = withWhom; | ||
45 | this.weak = weak; | ||
46 | } | ||
47 | |||
48 | // private Inequality( | ||
49 | // PSystem<PatternDescription, StubHandle, ?> pSystem, | ||
50 | // PVariable subject, Set<PVariable> inequals) | ||
51 | // { | ||
52 | // super(pSystem, include(inequals, subject)); | ||
53 | // this.subject = subject; | ||
54 | // this.inequals = inequals; | ||
55 | // } | ||
56 | |||
57 | // private static HashSet<PVariable> include(Set<PVariable> inequals, PVariable subject) { | ||
58 | // HashSet<PVariable> hashSet = new HashSet<PVariable>(inequals); | ||
59 | // hashSet.add(subject); | ||
60 | // return hashSet; | ||
61 | // } | ||
62 | |||
63 | @Override | ||
64 | public Set<PVariable> getDeferringVariables() { | ||
65 | return getAffectedVariables(); | ||
66 | } | ||
67 | |||
68 | // private static int[] mapIndices(Map<Object, Integer> variablesIndex, Set<PVariable> keys) { | ||
69 | // int[] result = new int[keys.size()]; | ||
70 | // int k = 0; | ||
71 | // for (PVariable key : keys) { | ||
72 | // result[k++] = variablesIndex.get(key); | ||
73 | // } | ||
74 | // return result; | ||
75 | // } | ||
76 | |||
77 | // @Override | ||
78 | // public IFoldablePConstraint getIncorporator() { | ||
79 | // return incorporator; | ||
80 | // } | ||
81 | // | ||
82 | // @Override | ||
83 | // public void registerIncorporatationInto(IFoldablePConstraint incorporator) { | ||
84 | // this.incorporator = incorporator; | ||
85 | // } | ||
86 | // | ||
87 | // @Override | ||
88 | // public boolean incorporate(IFoldablePConstraint other) { | ||
89 | // if (other instanceof Inequality<?, ?>) { | ||
90 | // Inequality other2 = (Inequality) other; | ||
91 | // if (subject.equals(other2.subject)) { | ||
92 | // Set<PVariable> newInequals = new HashSet<PVariable>(inequals); | ||
93 | // newInequals.addAll(other2.inequals); | ||
94 | // return new Inequality<PatternDescription, StubHandle>(buildable, subject, newInequals); | ||
95 | // } | ||
96 | // } else return false; | ||
97 | // } | ||
98 | |||
99 | @Override | ||
100 | protected String toStringRest() { | ||
101 | return who.toString() + (isWeak() ? "!=?" : "!=") + withWhom.toString(); | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
106 | if (obsolete.equals(who)) | ||
107 | who = replacement; | ||
108 | if (obsolete.equals(withWhom)) | ||
109 | withWhom = replacement; | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public Set<PVariable> getDeducedVariables() { | ||
114 | return Collections.emptySet(); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is | ||
119 | * undeducible. | ||
120 | * | ||
121 | * @return the weak | ||
122 | */ | ||
123 | public boolean isWeak() { | ||
124 | return weak; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * A weak inequality constraint is eliminable if who is the same as withWhom, or if any if them is undeducible. | ||
129 | */ | ||
130 | public boolean isEliminable() { | ||
131 | return isWeak() && (who.equals(withWhom) || !who.isDeducable() || !withWhom.isDeducable()); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Eliminates a weak inequality constraint if it can be ignored when who is the same as withWhom, or if any if them | ||
136 | * is undeducible. | ||
137 | */ | ||
138 | public void eliminateWeak() { | ||
139 | if (isEliminable()) | ||
140 | delete(); | ||
141 | } | ||
142 | |||
143 | public PVariable getWho() { | ||
144 | return who; | ||
145 | } | ||
146 | |||
147 | public PVariable getWithWhom() { | ||
148 | return withWhom; | ||
149 | } | ||
150 | |||
151 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java new file mode 100644 index 00000000..87d9d9fc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java | |||
@@ -0,0 +1,52 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | */ | ||
24 | public class NegativePatternCall extends PatternCallBasedDeferred { | ||
25 | |||
26 | public NegativePatternCall(PBody pBody, Tuple actualParametersTuple, PQuery query) { | ||
27 | super(pBody, actualParametersTuple, query); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Set<PVariable> getDeducedVariables() { | ||
32 | return Collections.emptySet(); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * @return all variables that may potentially be quantified they are not used anywhere else | ||
37 | */ | ||
38 | @Override | ||
39 | protected Set<PVariable> getCandidateQuantifiedVariables() { | ||
40 | return getAffectedVariables(); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | protected String toStringRest() { | ||
49 | return "!" + query.getFullyQualifiedName() + "@" + actualParametersTuple.toString(); | ||
50 | } | ||
51 | |||
52 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java new file mode 100644 index 00000000..93eeffec --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java | |||
@@ -0,0 +1,118 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | |||
25 | /** | ||
26 | * @author Gabor Bergmann | ||
27 | * | ||
28 | */ | ||
29 | public abstract class PatternCallBasedDeferred extends VariableDeferredPConstraint implements IQueryReference { | ||
30 | |||
31 | protected Tuple actualParametersTuple; | ||
32 | |||
33 | protected abstract void doDoReplaceVariables(PVariable obsolete, PVariable replacement); | ||
34 | |||
35 | protected abstract Set<PVariable> getCandidateQuantifiedVariables(); | ||
36 | |||
37 | protected PQuery query; | ||
38 | private Set<PVariable> deferringVariables; | ||
39 | |||
40 | public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, | ||
41 | PQuery pattern, Set<PVariable> additionalAffectedVariables) { | ||
42 | super(pBody, union(actualParametersTuple.<PVariable> getDistinctElements(), additionalAffectedVariables)); | ||
43 | this.actualParametersTuple = actualParametersTuple; | ||
44 | this.query = pattern; | ||
45 | } | ||
46 | |||
47 | public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, | ||
48 | PQuery pattern) { | ||
49 | this(pBody, actualParametersTuple, pattern, Collections.<PVariable> emptySet()); | ||
50 | } | ||
51 | |||
52 | private static Set<PVariable> union(Set<PVariable> a, Set<PVariable> b) { | ||
53 | Set<PVariable> result = new HashSet<PVariable>(); | ||
54 | result.addAll(a); | ||
55 | result.addAll(b); | ||
56 | return result; | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public Set<PVariable> getDeferringVariables() { | ||
61 | if (deferringVariables == null) { | ||
62 | deferringVariables = new HashSet<PVariable>(); | ||
63 | for (PVariable var : getCandidateQuantifiedVariables()) { | ||
64 | if (var.isDeducable()) | ||
65 | deferringVariables.add(var); | ||
66 | } | ||
67 | } | ||
68 | return deferringVariables; | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public void checkSanity() { | ||
73 | super.checkSanity(); | ||
74 | for (Object obj : this.actualParametersTuple.getDistinctElements()) { | ||
75 | PVariable var = (PVariable) obj; | ||
76 | if (!getDeferringVariables().contains(var)) { | ||
77 | // so this is a free variable of the NAC / aggregation? | ||
78 | for (PConstraint pConstraint : var.getReferringConstraints()) { | ||
79 | if (pConstraint != this | ||
80 | && !(pConstraint instanceof Equality && ((Equality) pConstraint).isMoot())) | ||
81 | throw new QueryProcessingException ( | ||
82 | "Variable {1} of constraint {2} is not a positively determined part of the pattern, yet it is also affected by {3}.", | ||
83 | new String[] { var.toString(), this.toString(), pConstraint.toString() }, | ||
84 | "Read-only variable can not be deduced", null); | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | } | ||
90 | |||
91 | // public SubPlan getSidePlan(IOperationCompiler compiler) throws QueryPlannerException { | ||
92 | // SubPlan sidePlan = compiler.patternCallPlan(actualParametersTuple, query); | ||
93 | // sidePlan = BuildHelper.enforceVariableCoincidences(compiler, sidePlan); | ||
94 | // return sidePlan; | ||
95 | // } | ||
96 | |||
97 | @Override | ||
98 | protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
99 | if (deferringVariables != null) { | ||
100 | // FAIL instead of hopeless attempt to fix | ||
101 | // if (deferringVariables.remove(obsolete)) deferringVariables.add(replacement); | ||
102 | throw new IllegalStateException("Cannot replace variables on " + this | ||
103 | + " when deferring variables have already been identified."); | ||
104 | } | ||
105 | actualParametersTuple = actualParametersTuple.replaceAll(obsolete, replacement); | ||
106 | doDoReplaceVariables(obsolete, replacement); | ||
107 | } | ||
108 | |||
109 | public Tuple getActualParametersTuple() { | ||
110 | return actualParametersTuple; | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public PQuery getReferredQuery() { | ||
115 | return query; | ||
116 | } | ||
117 | |||
118 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java new file mode 100644 index 00000000..0c40d91e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java | |||
@@ -0,0 +1,70 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
22 | |||
23 | /** | ||
24 | * @author Gabor Bergmann | ||
25 | */ | ||
26 | public class PatternMatchCounter extends PatternCallBasedDeferred { | ||
27 | |||
28 | private PVariable resultVariable; | ||
29 | |||
30 | public PatternMatchCounter(PBody pBody, Tuple actualParametersTuple, | ||
31 | PQuery query, PVariable resultVariable) { | ||
32 | super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); | ||
33 | this.resultVariable = resultVariable; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Set<PVariable> getDeducedVariables() { | ||
38 | return Collections.singleton(resultVariable); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
43 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
44 | result.put(getDeferringVariables(), getDeducedVariables()); | ||
45 | return result; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { | ||
50 | if (resultVariable.equals(obsolete)) | ||
51 | resultVariable = replacement; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | protected Set<PVariable> getCandidateQuantifiedVariables() { | ||
56 | return actualParametersTuple.<PVariable> getDistinctElements(); | ||
57 | } | ||
58 | |||
59 | |||
60 | @Override | ||
61 | protected String toStringRest() { | ||
62 | return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" | ||
63 | + resultVariable.toString(); | ||
64 | } | ||
65 | |||
66 | public PVariable getResultVariable() { | ||
67 | return resultVariable; | ||
68 | } | ||
69 | |||
70 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java new file mode 100644 index 00000000..336a83fb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2022, Tamas Szabo, GitHub | ||
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.basicdeferred; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | |||
20 | /** | ||
21 | * A constraint which prescribes the evaluation of custom Java logic that takes an arbitrary number of input relations | ||
22 | * and produces one output relation. Contrast this to {@link ExpressionEvaluation}, which produces a single output value | ||
23 | * given an input tuple. | ||
24 | * | ||
25 | * The assumption is that the relation evaluation logic is not incremental, that is, it can only perform from-scratch | ||
26 | * computation of the output relation given the complete input relations. To this end, the relation evaluator always | ||
27 | * receives the complete input relations with all their contents as input. However, the evaluator engine makes sure that | ||
28 | * the output of the relation evaluation is at least "seemingly" incremental. This means that the underlying computation | ||
29 | * network computes the delta on the output compared to the previous output and only propagates the delta further. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * | ||
33 | * @since 2.8 | ||
34 | * | ||
35 | */ | ||
36 | public class RelationEvaluation extends EnumerablePConstraint implements IMultiQueryReference { | ||
37 | |||
38 | private final IRelationEvaluator evaluator; | ||
39 | private final List<PQuery> inputQueries; | ||
40 | |||
41 | public RelationEvaluation(final PBody body, final Tuple variablesTuple, final List<PQuery> inputQueries, | ||
42 | final IRelationEvaluator evaluator) { | ||
43 | super(body, variablesTuple); | ||
44 | this.evaluator = evaluator; | ||
45 | this.inputQueries = inputQueries; | ||
46 | } | ||
47 | |||
48 | public IRelationEvaluator getEvaluator() { | ||
49 | return this.evaluator; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public List<PQuery> getReferredQueries() { | ||
54 | return this.inputQueries; | ||
55 | } | ||
56 | |||
57 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java new file mode 100644 index 00000000..8b6e29ef --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java | |||
@@ -0,0 +1,105 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.basicdeferred; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | |||
25 | /** | ||
26 | * Represents a non-enumerable type constraint that asserts that values substituted for the given tuple of variables | ||
27 | * form a tuple that belongs to a (typically non-enumerable) extensional relation identified by an {@link IInputKey}. | ||
28 | * | ||
29 | * <p> The InputKey is typically not enumerable. If it is enumerable, use {@link TypeConstraint} instead, so that the PConstraint carries over the property of enumerability. | ||
30 | * | ||
31 | * @author Bergmann Gabor | ||
32 | * | ||
33 | */ | ||
34 | public class TypeFilterConstraint extends VariableDeferredPConstraint implements | ||
35 | ITypeConstraint { | ||
36 | |||
37 | private Tuple variablesTuple; | ||
38 | private IInputKey inputKey; | ||
39 | |||
40 | private TypeJudgement equivalentJudgement; | ||
41 | |||
42 | |||
43 | public TypeFilterConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { | ||
44 | super(pBody, variablesTuple.<PVariable> getDistinctElements()); | ||
45 | this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); | ||
46 | |||
47 | this.variablesTuple = variablesTuple; | ||
48 | this.inputKey = inputKey; | ||
49 | |||
50 | if (variablesTuple.getSize() != inputKey.getArity()) | ||
51 | throw new IllegalArgumentException( | ||
52 | this.getClass().getSimpleName() + | ||
53 | " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + | ||
54 | inputKey); | ||
55 | } | ||
56 | |||
57 | |||
58 | |||
59 | public Tuple getVariablesTuple() { | ||
60 | return variablesTuple; | ||
61 | } | ||
62 | |||
63 | public IInputKey getInputKey() { | ||
64 | return inputKey; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public TypeJudgement getEquivalentJudgement() { | ||
69 | return equivalentJudgement; | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
74 | variablesTuple = variablesTuple.replaceAll(obsolete, replacement); | ||
75 | this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
80 | return Collections.singleton(equivalentJudgement); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public Set<PVariable> getDeducedVariables() { | ||
85 | return Collections.emptySet(); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public Set<PVariable> getDeferringVariables() { | ||
90 | return getAffectedVariables(); | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | protected String toStringRest() { | ||
95 | return inputKey.getPrettyPrintableName() + "@" + variablesTuple; | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
100 | return TypeConstraintUtil.getFunctionalDependencies(context, inputKey, variablesTuple); | ||
101 | } | ||
102 | |||
103 | |||
104 | |||
105 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java new file mode 100644 index 00000000..7bbf7118 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.basicenumerables; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | |||
22 | /** | ||
23 | * @since 2.0 | ||
24 | */ | ||
25 | public abstract class AbstractTransitiveClosure extends KeyedEnumerablePConstraint<PQuery> implements IQueryReference, ITypeInfoProviderConstraint { | ||
26 | |||
27 | public AbstractTransitiveClosure(PBody pBody, Tuple variablesTuple, PQuery supplierKey) { | ||
28 | super(pBody, variablesTuple, supplierKey); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public PQuery getReferredQuery() { | ||
33 | return supplierKey; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * @since 1.3 | ||
38 | */ | ||
39 | @Override | ||
40 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
41 | return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); | ||
42 | } | ||
43 | |||
44 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java new file mode 100644 index 00000000..e3dae240 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Zoltan Ujhelyi, Gabor Bergmann, IncQuery Labs Ltd. | ||
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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
13 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | |||
18 | /** | ||
19 | * For a binary base pattern over an enumerable universe type, computes the reflexive transitive closure (base)* | ||
20 | * | ||
21 | * @author Gabor Bergmann, Zoltan Ujhelyi | ||
22 | * @since 2.0 | ||
23 | */ | ||
24 | public class BinaryReflexiveTransitiveClosure extends AbstractTransitiveClosure { | ||
25 | |||
26 | private final IInputKey universeType; | ||
27 | |||
28 | public BinaryReflexiveTransitiveClosure(PBody pBody, Tuple variablesTuple, | ||
29 | PQuery pattern, IInputKey universeType) { | ||
30 | super(pBody, variablesTuple, pattern); | ||
31 | this.universeType = universeType; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | protected String keyToString() { | ||
36 | return supplierKey.getFullyQualifiedName() + "*"; | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Returns the type whose instances should be returned as 0-long paths. | ||
41 | * @since 2.0 | ||
42 | */ | ||
43 | public IInputKey getUniverseType() { | ||
44 | return universeType; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public void checkSanity() { | ||
49 | if (!universeType.isEnumerable() || universeType.getArity() != 1) { | ||
50 | throw new QueryProcessingException( | ||
51 | String.format("Invalid universe type %s - it should be enumerable and must have an arity of 1.", | ||
52 | universeType.getPrettyPrintableName()), | ||
53 | pBody.getPattern()); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java new file mode 100644 index 00000000..716d043b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | |||
16 | /** | ||
17 | * For a binary base pattern, computes the irreflexive transitive closure (base)+ | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public class BinaryTransitiveClosure extends AbstractTransitiveClosure { | ||
23 | |||
24 | public BinaryTransitiveClosure(PBody pBody, Tuple variablesTuple, | ||
25 | PQuery pattern) { | ||
26 | super(pBody, variablesTuple, pattern); | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | protected String keyToString() { | ||
31 | return supplierKey.getFullyQualifiedName() + "+"; | ||
32 | } | ||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java new file mode 100644 index 00000000..10da2e21 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java | |||
@@ -0,0 +1,11 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
7 | |||
8 | public enum Connectivity { | ||
9 | WEAK, | ||
10 | STRONG; | ||
11 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java new file mode 100644 index 00000000..251146c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java | |||
@@ -0,0 +1,57 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | |||
23 | /** | ||
24 | * @author Gabor Bergmann | ||
25 | * | ||
26 | */ | ||
27 | public class ConstantValue extends KeyedEnumerablePConstraint<Object> { | ||
28 | |||
29 | private PVariable variable; | ||
30 | |||
31 | public ConstantValue(PBody pBody, PVariable variable, Object value) { | ||
32 | super(pBody, Tuples.staticArityFlatTupleOf(variable), value); | ||
33 | this.variable = variable; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | protected String keyToString() { | ||
38 | return supplierKey.toString(); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * @since 1.7 | ||
43 | */ | ||
44 | public PVariable getVariable() { | ||
45 | return variable; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
50 | final Map<Set<PVariable>, Set<PVariable>> result = new HashMap<Set<PVariable>, Set<PVariable>>(); | ||
51 | final Set<PVariable> emptySet = Collections.emptySet(); // a constant value is functionally determined by everything | ||
52 | result.put(emptySet, Collections.singleton(getVariableInTuple(0))); | ||
53 | return result; | ||
54 | } | ||
55 | |||
56 | |||
57 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java new file mode 100644 index 00000000..25ab34b4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
11 | |||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * @author Gabor Bergmann | ||
28 | * | ||
29 | */ | ||
30 | public class PositivePatternCall extends KeyedEnumerablePConstraint<PQuery> implements IQueryReference, ITypeInfoProviderConstraint { | ||
31 | |||
32 | public PositivePatternCall(PBody pBody, Tuple variablesTuple, | ||
33 | PQuery pattern) { | ||
34 | super(pBody, variablesTuple, pattern); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | protected String keyToString() { | ||
39 | return supplierKey.getFullyQualifiedName(); | ||
40 | } | ||
41 | |||
42 | // Note: #getFunctionalDependencies is intentionally not implemented - use QueryAnalyzer instead! | ||
43 | // @Override | ||
44 | // public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
45 | // return super.getFunctionalDependencies(context); | ||
46 | // } | ||
47 | |||
48 | @Override | ||
49 | public PQuery getReferredQuery() { | ||
50 | return supplierKey; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
55 | return getTypesImpliedByCall(supplierKey, variablesTuple); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * @since 1.3 | ||
60 | */ | ||
61 | public static Set<TypeJudgement> getTypesImpliedByCall(PQuery calledQuery, Tuple actualParametersTuple) { | ||
62 | Set<TypeJudgement> result = new HashSet<TypeJudgement>(); | ||
63 | for (TypeJudgement parameterJudgement : calledQuery.getTypeGuarantees()) { | ||
64 | IInputKey inputKey = parameterJudgement.getInputKey(); | ||
65 | Tuple judgementIndexTuple = parameterJudgement.getVariablesTuple(); | ||
66 | |||
67 | Object[] judgementVariables = new Object[judgementIndexTuple.getSize()]; | ||
68 | for (int i=0; i<judgementVariables.length; ++i) | ||
69 | judgementVariables[i] = actualParametersTuple.get((int) judgementIndexTuple.get(i)); | ||
70 | |||
71 | result.add(new TypeJudgement(inputKey, Tuples.flatTupleOf(judgementVariables))); | ||
72 | } | ||
73 | return result; | ||
74 | } | ||
75 | |||
76 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java new file mode 100644 index 00000000..b97ff55f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java | |||
@@ -0,0 +1,43 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
9 | import tools.refinery.viatra.runtime.matchers.psystem.*; | ||
10 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | |||
13 | import java.util.Set; | ||
14 | |||
15 | public class RepresentativeElectionConstraint extends KeyedEnumerablePConstraint<PQuery> | ||
16 | implements IQueryReference, ITypeInfoProviderConstraint { | ||
17 | private final Connectivity connectivity; | ||
18 | |||
19 | public RepresentativeElectionConstraint(PBody pBody, Tuple variablesTuple, PQuery supplierKey, | ||
20 | Connectivity connectivity) { | ||
21 | super(pBody, variablesTuple, supplierKey); | ||
22 | this.connectivity = connectivity; | ||
23 | } | ||
24 | |||
25 | public Connectivity getConnectivity() { | ||
26 | return connectivity; | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public PQuery getReferredQuery() { | ||
31 | return supplierKey; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
36 | return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | protected String keyToString() { | ||
41 | return supplierKey.getFullyQualifiedName() + "#" + connectivity + "#representative"; | ||
42 | } | ||
43 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java new file mode 100644 index 00000000..2ca54cc0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java | |||
@@ -0,0 +1,79 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.basicenumerables; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
23 | |||
24 | /** | ||
25 | * Represents an enumerable type constraint that asserts that values substituted for the given tuple of variables | ||
26 | * form a tuple that belongs to an enumerable extensional relation identified by an {@link IInputKey}. | ||
27 | * | ||
28 | * <p> The InputKey must be enumerable! | ||
29 | * | ||
30 | * @author Zoltan Ujhelyi | ||
31 | * | ||
32 | */ | ||
33 | public class TypeConstraint extends KeyedEnumerablePConstraint<IInputKey> implements ITypeConstraint { | ||
34 | |||
35 | private TypeJudgement equivalentJudgement; | ||
36 | |||
37 | public TypeConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { | ||
38 | super(pBody, variablesTuple, inputKey); | ||
39 | this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); | ||
40 | |||
41 | if (! inputKey.isEnumerable()) | ||
42 | throw new IllegalArgumentException( | ||
43 | this.getClass().getSimpleName() + | ||
44 | " applicable for enumerable input keys only; received instead " + | ||
45 | inputKey); | ||
46 | if (variablesTuple.getSize() != inputKey.getArity()) | ||
47 | throw new IllegalArgumentException( | ||
48 | this.getClass().getSimpleName() + | ||
49 | " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + | ||
50 | inputKey); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | protected String keyToString() { | ||
55 | return supplierKey.getPrettyPrintableName(); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public TypeJudgement getEquivalentJudgement() { | ||
60 | return equivalentJudgement; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Set<TypeJudgement> getImpliedJudgements(IQueryMetaContext context) { | ||
65 | return Collections.singleton(equivalentJudgement); | ||
66 | //return equivalentJudgement.getDirectlyImpliedJudgements(context); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public Map<Set<PVariable>, Set<PVariable>> getFunctionalDependencies(IQueryMetaContext context) { | ||
71 | return TypeConstraintUtil.getFunctionalDependencies(context, supplierKey, variablesTuple); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void doReplaceVariable(PVariable obsolete, PVariable replacement) { | ||
76 | super.doReplaceVariable(obsolete, replacement); | ||
77 | this.equivalentJudgement = new TypeJudgement(getSupplierKey(), variablesTuple); | ||
78 | } | ||
79 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java new file mode 100644 index 00000000..2c03a894 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java | |||
@@ -0,0 +1,231 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.queries; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Optional; | ||
17 | import java.util.Set; | ||
18 | import java.util.stream.Collectors; | ||
19 | import java.util.stream.Stream; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
22 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
24 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
28 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
29 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
30 | |||
31 | /** | ||
32 | * Default implementation of PQuery. | ||
33 | * | ||
34 | * @author Bergmann Gabor | ||
35 | */ | ||
36 | public abstract class BasePQuery implements PQuery { | ||
37 | |||
38 | protected PQueryStatus status = PQueryStatus.UNINITIALIZED; | ||
39 | /** | ||
40 | * @since 2.0 | ||
41 | */ | ||
42 | protected final PVisibility visibility; | ||
43 | protected List<PProblem> pProblems = new ArrayList<PProblem>(); | ||
44 | private List<PAnnotation> annotations = new ArrayList<PAnnotation>(); | ||
45 | private QueryEvaluationHint evaluationHints = new QueryEvaluationHint(null, (IQueryBackendFactory)null); | ||
46 | PDisjunction canonicalDisjunction; | ||
47 | private List<String> parameterNames = null; // Lazy initialization | ||
48 | |||
49 | /** For traceability only. */ | ||
50 | private List<Object> wrappingQuerySpecifications = new ArrayList<Object>(1); | ||
51 | |||
52 | @Override | ||
53 | public Integer getPositionOfParameter(String parameterName) { | ||
54 | ensureInitialized(); | ||
55 | int index = getParameterNames().indexOf(parameterName); | ||
56 | return index != -1 ? index : null; | ||
57 | } | ||
58 | |||
59 | protected void setStatus(PQueryStatus newStatus) { | ||
60 | this.status = newStatus; | ||
61 | } | ||
62 | |||
63 | protected void addError(PProblem problem) { | ||
64 | status = PQueryStatus.ERROR; | ||
65 | pProblems.add(problem); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public PQueryStatus getStatus() { | ||
70 | return status; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public List<PProblem> getPProblems() { | ||
75 | return Collections.unmodifiableList(pProblems); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public boolean isMutable() { | ||
80 | return status.equals(PQueryStatus.UNINITIALIZED) || status.equals(PQueryStatus.INITIALIZING); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public void checkMutability() { | ||
85 | Preconditions.checkState(isMutable(), "Cannot edit query definition %s", getFullyQualifiedName()); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * @since 1.5 | ||
90 | */ | ||
91 | public void setEvaluationHints(QueryEvaluationHint hints) { | ||
92 | checkMutability(); | ||
93 | this.evaluationHints = hints; | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public QueryEvaluationHint getEvaluationHints() { | ||
98 | ensureInitialized(); | ||
99 | return evaluationHints; | ||
100 | // TODO instead of field, compute something from annotations? | ||
101 | } | ||
102 | |||
103 | protected void addAnnotation(PAnnotation annotation) { | ||
104 | checkMutability(); | ||
105 | annotations.add(annotation); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public List<PAnnotation> getAllAnnotations() { | ||
110 | ensureInitialized(); | ||
111 | return new ArrayList<>(annotations); | ||
112 | } | ||
113 | |||
114 | private Stream<PAnnotation> getAnnotationStreamByName(final String name) { | ||
115 | ensureInitialized(); | ||
116 | return annotations.stream().filter(Objects::nonNull).filter(annotation -> Objects.equals(name, annotation.getName())); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public List<PAnnotation> getAnnotationsByName(final String annotationName) { | ||
121 | return getAnnotationStreamByName(annotationName).collect(Collectors.toList()); | ||
122 | } | ||
123 | |||
124 | @Override | ||
125 | public Optional<PAnnotation> getFirstAnnotationByName(String annotationName) { | ||
126 | return getAnnotationStreamByName(annotationName).findFirst(); | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public List<String> getParameterNames() { | ||
131 | ensureInitialized(); | ||
132 | if (parameterNames == null) { | ||
133 | parameterNames = getParameters().stream().map(PParameter::getName).collect(Collectors.toList()); | ||
134 | } | ||
135 | return parameterNames; | ||
136 | } | ||
137 | |||
138 | @Override | ||
139 | public Set<PQuery> getDirectReferredQueries() { | ||
140 | ensureInitialized(); | ||
141 | return canonicalDisjunction.getDirectReferredQueries(); | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public Set<PQuery> getAllReferredQueries() { | ||
146 | ensureInitialized(); | ||
147 | return canonicalDisjunction.getAllReferredQueries(); | ||
148 | } | ||
149 | |||
150 | |||
151 | @Override | ||
152 | public List<Object> publishedAs() { | ||
153 | return wrappingQuerySpecifications; | ||
154 | } | ||
155 | |||
156 | @Override | ||
157 | public Set<TypeJudgement> getTypeGuarantees() { | ||
158 | ensureInitialized(); | ||
159 | Set<TypeJudgement> result = new HashSet<TypeJudgement>(); | ||
160 | |||
161 | List<PParameter> parameters = getParameters(); | ||
162 | for (int i=0; i<parameters.size(); ++i) { | ||
163 | PParameter parameter = parameters.get(i); | ||
164 | IInputKey declaredUnaryType = parameter.getDeclaredUnaryType(); | ||
165 | if (declaredUnaryType != null) { | ||
166 | result.add(new TypeJudgement(declaredUnaryType, Tuples.staticArityFlatTupleOf(i))); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | return result; | ||
171 | } | ||
172 | |||
173 | /** | ||
174 | * @since 2.0 | ||
175 | */ | ||
176 | public BasePQuery(PVisibility visibility) { | ||
177 | super(); | ||
178 | this.visibility = visibility; | ||
179 | } | ||
180 | |||
181 | @Override | ||
182 | public PDisjunction getDisjunctBodies() { | ||
183 | ensureInitialized(); | ||
184 | Preconditions.checkState(!status.equals(PQueryStatus.ERROR), "Query %s contains errors.", getFullyQualifiedName()); | ||
185 | return canonicalDisjunction; | ||
186 | } | ||
187 | |||
188 | @Override | ||
189 | public final void ensureInitialized() { | ||
190 | try { | ||
191 | if (status.equals(PQueryStatus.UNINITIALIZED)) { | ||
192 | setStatus(PQueryStatus.INITIALIZING); | ||
193 | setBodies(doGetContainedBodies()); | ||
194 | setStatus(PQueryStatus.OK); | ||
195 | } | ||
196 | } catch (QueryInitializationException e) { | ||
197 | addError(new PProblem(e, e.getShortMessage())); | ||
198 | throw e; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | protected final void setBodies(Set<PBody> bodies) { | ||
203 | canonicalDisjunction = new PDisjunction(this, bodies); | ||
204 | for (PBody body : canonicalDisjunction.getBodies()) { | ||
205 | body.setStatus(null); | ||
206 | } | ||
207 | setStatus(PQueryStatus.OK); | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * Creates and returns the bodies of the query. If recalled again, a new instance is created. | ||
212 | * | ||
213 | * @return | ||
214 | * @throws ViatraQueryRuntimeException | ||
215 | */ | ||
216 | protected abstract Set<PBody> doGetContainedBodies(); | ||
217 | |||
218 | @Override | ||
219 | public String toString() { | ||
220 | return String.format("PQuery<%s>=%s", getFullyQualifiedName(), super.toString()); | ||
221 | } | ||
222 | |||
223 | /** | ||
224 | * @since 2.0 | ||
225 | */ | ||
226 | @Override | ||
227 | public PVisibility getVisibility() { | ||
228 | return visibility; | ||
229 | } | ||
230 | |||
231 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java new file mode 100644 index 00000000..eae4eacf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java | |||
@@ -0,0 +1,104 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.queries; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.LinkedHashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.stream.Collectors; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | |||
18 | /** | ||
19 | * | ||
20 | * A disjunction is a set of bodies representing separate conditions. A {@link PQuery} has a single, canonical | ||
21 | * PDisjunction, that can be replaced using rewriter | ||
22 | * | ||
23 | * @author Zoltan Ujhelyi | ||
24 | * | ||
25 | */ | ||
26 | public class PDisjunction { | ||
27 | |||
28 | private Set<PBody> bodies; | ||
29 | private PQuery query; | ||
30 | |||
31 | public PDisjunction(Set<PBody> bodies) { | ||
32 | this(bodies.iterator().next().getPattern(), bodies); | ||
33 | } | ||
34 | |||
35 | public PDisjunction(PQuery query, Set<PBody> bodies) { | ||
36 | super(); | ||
37 | this.query = query; | ||
38 | this.bodies = Collections.unmodifiableSet(new LinkedHashSet<>(bodies)); | ||
39 | this.bodies.forEach(body -> body.setContainerDisjunction(this)); | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * Returns an immutable set of bodies that consists of this disjunction | ||
44 | * | ||
45 | * @return the bodies | ||
46 | */ | ||
47 | public Set<PBody> getBodies() { | ||
48 | return bodies; | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Returns the corresponding query specification. May be null if not set. | ||
53 | */ | ||
54 | public PQuery getQuery() { | ||
55 | return query; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * Returns all queries directly referred in the constraints. They are all required to evaluate this query | ||
60 | * | ||
61 | * @return a non-null, but possibly empty list of query definitions | ||
62 | */ | ||
63 | public Set<PQuery> getDirectReferredQueries() { | ||
64 | return this.getBodies().stream(). | ||
65 | flatMap(PQueries.directlyReferencedQueriesFunction()). // flatten stream of streams | ||
66 | collect(Collectors.toCollection(LinkedHashSet::new)); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Returns all queries required to evaluate this query (transitively). | ||
71 | * | ||
72 | * @return a non-null, but possibly empty list of query definitions | ||
73 | */ | ||
74 | public Set<PQuery> getAllReferredQueries() { | ||
75 | Set<PQuery> processedQueries = new LinkedHashSet<>(); | ||
76 | processedQueries.add(this.getQuery()); | ||
77 | Set<PQuery> foundQueries = getDirectReferredQueries(); | ||
78 | Set<PQuery> newQueries = new LinkedHashSet<>(foundQueries); | ||
79 | |||
80 | while(!processedQueries.containsAll(newQueries)) { | ||
81 | PQuery query = newQueries.iterator().next(); | ||
82 | processedQueries.add(query); | ||
83 | newQueries.remove(query); | ||
84 | Set<PQuery> referred = query.getDirectReferredQueries(); | ||
85 | referred.removeAll(processedQueries); | ||
86 | foundQueries.addAll(referred); | ||
87 | newQueries.addAll(referred); | ||
88 | } | ||
89 | return foundQueries; | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Decides whether a disjunction is mutable. A disjunction is mutable if all its contained bodies are mutable. | ||
94 | * | ||
95 | */ | ||
96 | public boolean isMutable() { | ||
97 | for (PBody body : bodies) { | ||
98 | if (!body.isMutable()) { | ||
99 | return false; | ||
100 | } | ||
101 | } | ||
102 | return true; | ||
103 | } | ||
104 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java new file mode 100644 index 00000000..07165aa2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java | |||
@@ -0,0 +1,105 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.queries; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
14 | |||
15 | /** | ||
16 | * A descriptor for declared PQuery parameters. A parameter has a name, a declared type and a direction constraint | ||
17 | * | ||
18 | * @author Zoltan Ujhelyi | ||
19 | * | ||
20 | */ | ||
21 | public class PParameter { | ||
22 | |||
23 | private final String name; | ||
24 | private final String typeName; | ||
25 | private final IInputKey declaredUnaryType; | ||
26 | private final PParameterDirection direction; | ||
27 | |||
28 | public PParameter(String name) { | ||
29 | this(name, (String) null); | ||
30 | } | ||
31 | |||
32 | public PParameter(String name, String typeName) { | ||
33 | this(name, typeName, (IInputKey) null); | ||
34 | } | ||
35 | |||
36 | public PParameter(String name, String typeName, IInputKey declaredUnaryType) { | ||
37 | this(name, typeName, declaredUnaryType, PParameterDirection.INOUT); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * @since 1.4 | ||
42 | */ | ||
43 | public PParameter(String name, String typeName, IInputKey declaredUnaryType, PParameterDirection direction) { | ||
44 | super(); | ||
45 | this.name = name; | ||
46 | this.typeName = typeName; | ||
47 | this.declaredUnaryType = declaredUnaryType; | ||
48 | this.direction = direction; | ||
49 | |||
50 | if (declaredUnaryType != null && declaredUnaryType.getArity() != 1) { | ||
51 | throw new IllegalArgumentException( | ||
52 | "PParameter declared type must be unary instead of " + declaredUnaryType.getPrettyPrintableName()); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * @return the direction | ||
58 | * @since 1.4 | ||
59 | */ | ||
60 | public PParameterDirection getDirection() { | ||
61 | return direction; | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * @return the name of the parameter | ||
66 | */ | ||
67 | public String getName() { | ||
68 | return name; | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Returns a textual representation of the declared type of the parameter | ||
73 | * | ||
74 | * @return the type description, or null if not available | ||
75 | */ | ||
76 | public String getTypeName() { | ||
77 | return typeName; | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * Yield an {@link IInputKey} representation of the type declared for this parameter. | ||
82 | * | ||
83 | * @return the unary type that was declared on this parameter in the query header, or null if not available | ||
84 | */ | ||
85 | public IInputKey getDeclaredUnaryType() { | ||
86 | return declaredUnaryType; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public boolean equals(Object obj) { | ||
91 | if (obj instanceof PParameter) { | ||
92 | return Objects.equals(name, ((PParameter) obj).name) | ||
93 | && Objects.equals(typeName, ((PParameter) obj).typeName) | ||
94 | && Objects.equals(declaredUnaryType, ((PParameter) obj).declaredUnaryType) | ||
95 | && Objects.equals(direction, ((PParameter) obj).direction); | ||
96 | } | ||
97 | return false; | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public int hashCode() { | ||
102 | return Objects.hash(name, typeName, declaredUnaryType); | ||
103 | } | ||
104 | |||
105 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java new file mode 100644 index 00000000..c94d4797 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.queries; | ||
10 | |||
11 | /** | ||
12 | * Values of this enum describe a constraint to the calling of patterns regarding its parameters. | ||
13 | * | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public enum PParameterDirection { | ||
19 | |||
20 | /** | ||
21 | * Default value, no additional constraint is applied | ||
22 | */ | ||
23 | INOUT, | ||
24 | |||
25 | /** | ||
26 | * The parameters marked with this constraints shall be set to a value before calling the pattern | ||
27 | */ | ||
28 | IN, | ||
29 | |||
30 | /** | ||
31 | * The parameters marked with this constraints shall not be set to a value before calling the pattern | ||
32 | */ | ||
33 | OUT | ||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java new file mode 100644 index 00000000..1fe4f541 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, 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.queries; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
12 | |||
13 | /** | ||
14 | * Represents an error that was detected while the {@link PQuery} object was built from a source. | ||
15 | * @author Bergmann Gabor | ||
16 | * | ||
17 | */ | ||
18 | public class PProblem { | ||
19 | |||
20 | private final String shortMessage; | ||
21 | private final String location; | ||
22 | private final Exception exception; | ||
23 | |||
24 | public PProblem(String shortMessage) { | ||
25 | this(null, shortMessage, null, null); | ||
26 | } | ||
27 | /** | ||
28 | * @since 2.0 | ||
29 | */ | ||
30 | public PProblem(String shortMessage, Integer line, Integer column) { | ||
31 | this(null, shortMessage, line, column); | ||
32 | } | ||
33 | public PProblem(QueryProcessingException exception) { | ||
34 | this(exception, exception.getShortMessage(), null, null); | ||
35 | } | ||
36 | public PProblem(Exception exception, String shortMessage) { | ||
37 | this(exception, shortMessage, null, null); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * @since 2.0 | ||
42 | */ | ||
43 | public PProblem(Exception exception, String shortMessage, Integer line, Integer column) { | ||
44 | this.shortMessage = shortMessage; | ||
45 | this.exception = exception; | ||
46 | if (line == null) { | ||
47 | location = "Unspecified location"; | ||
48 | } else if (column == null) { | ||
49 | location = String.format("Line %d", line); | ||
50 | } else { | ||
51 | location = String.format("Line %d Column %d", line, column); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | public String getShortMessage() { | ||
56 | return shortMessage; | ||
57 | } | ||
58 | public Exception getException() { | ||
59 | return exception; | ||
60 | } | ||
61 | /** | ||
62 | * @since 2.0 | ||
63 | */ | ||
64 | public String getLocation() { | ||
65 | return location; | ||
66 | } | ||
67 | |||
68 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java new file mode 100644 index 00000000..56f8ca76 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java | |||
@@ -0,0 +1,110 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.queries; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Set; | ||
13 | import java.util.function.Function; | ||
14 | import java.util.function.Predicate; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
24 | |||
25 | /** | ||
26 | * Utility class for using PQueries in functional/streaming collection operations effectively | ||
27 | * | ||
28 | * @author Zoltan Ujhelyi | ||
29 | * | ||
30 | */ | ||
31 | public final class PQueries { | ||
32 | |||
33 | /** | ||
34 | * Hidden constructor for utility class | ||
35 | */ | ||
36 | private PQueries() { | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Predicate checking for the status of selected queries | ||
41 | * | ||
42 | */ | ||
43 | public static Predicate<PQuery> queryStatusPredicate(final PQueryStatus status) { | ||
44 | return query -> query.getStatus().equals(status); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * Enumerates referred queries (without duplicates) for the given body | ||
49 | */ | ||
50 | public static Function<PBody, Stream<PQuery>> directlyReferencedQueriesFunction() { | ||
51 | return body -> (body.getConstraintsOfType(IMultiQueryReference.class).stream() | ||
52 | .flatMap(e -> e.getReferredQueries().stream()).distinct()); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Enumerates directly referred extensional relations (without duplicates) in the canonical form of the given query | ||
57 | * | ||
58 | * @param enumerablesOnly | ||
59 | * only enumerable type constraints are considered | ||
60 | * @since 2.0 | ||
61 | */ | ||
62 | public static Stream<IInputKey> directlyRequiredTypesOfQuery(PQuery query, boolean enumerablesOnly) { | ||
63 | return directlyRequiredTypesOfDisjunction(query.getDisjunctBodies(), enumerablesOnly); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Enumerates directly referred extensional relations (without duplicates) for the given formulation of a query. | ||
68 | * | ||
69 | * @param enumerablesOnly | ||
70 | * only enumerable type constraints are considered | ||
71 | * @since 2.0 | ||
72 | */ | ||
73 | public static Stream<IInputKey> directlyRequiredTypesOfDisjunction(PDisjunction disjunctBodies, | ||
74 | boolean enumerablesOnly) { | ||
75 | Class<? extends ITypeConstraint> filterClass = enumerablesOnly ? TypeConstraint.class : ITypeConstraint.class; | ||
76 | return disjunctBodies.getBodies().stream().flatMap(body -> body.getConstraintsOfType(filterClass).stream()) | ||
77 | .map(constraint -> constraint.getEquivalentJudgement().getInputKey()).distinct(); | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * @since 1.4 | ||
82 | */ | ||
83 | public static Predicate<PParameter> parameterDirectionPredicate(final PParameterDirection direction) { | ||
84 | return input -> input.getDirection() == direction; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Returns all {@link PTraceable}s contained in the given {@link PQuery}: itself, its bodies and their constraints. | ||
89 | * | ||
90 | * @since 1.6 | ||
91 | */ | ||
92 | public static Set<PTraceable> getTraceables(PQuery query) { | ||
93 | final Set<PTraceable> traceables = new HashSet<>(); | ||
94 | traceables.add(query); | ||
95 | query.getDisjunctBodies().getBodies().forEach(body -> { | ||
96 | traceables.add(body); | ||
97 | body.getConstraints().forEach(traceables::add); | ||
98 | }); | ||
99 | return traceables; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Calculates the simple name related from a given qualified name by finding the part after the last '.' character. | ||
104 | * | ||
105 | * @since 2.0 | ||
106 | */ | ||
107 | public static String calculateSimpleName(String qualifiedName) { | ||
108 | return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); | ||
109 | } | ||
110 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java new file mode 100644 index 00000000..a909c650 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java | |||
@@ -0,0 +1,154 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.queries; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
16 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
17 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
21 | |||
22 | /** | ||
23 | * Internal representation of a query / graph pattern (using a constraint system formalism), | ||
24 | * to be interpreted by a query evaluator ({@link IQueryBackend}). | ||
25 | * End-users of VIATRA Query should access a query as an IQuerySpecification instead. | ||
26 | * | ||
27 | * <p> | ||
28 | * PQuerys are definitions of queries usable inside pattern descriptions. Such description always has (a non-null) name. The query | ||
29 | * itself is defined as a (non-empty) set of {@link PBody} instances, the result is the disjunction of the single | ||
30 | * {@link PBody} instances. </p> | ||
31 | * <p> | ||
32 | * A PQuery might be constructed from erroneous patterns or might be uninitialized - this is represented by its status. | ||
33 | * | ||
34 | * @author Zoltan Ujhelyi | ||
35 | * @since 0.8.0 | ||
36 | * @noimplement This interface is not intended to be implemented by clients. Use {@link BasePQuery} as a base class instead. | ||
37 | */ | ||
38 | public interface PQuery extends PQueryHeader, PTraceable { | ||
39 | |||
40 | // TODO rewritten as / rewritten from traceability to PDisjunction? | ||
41 | |||
42 | /** | ||
43 | * @author Zoltan Ujhelyi | ||
44 | * | ||
45 | */ | ||
46 | public enum PQueryStatus { | ||
47 | /** | ||
48 | * Marks that the query definition is not initialized | ||
49 | */ | ||
50 | UNINITIALIZED, | ||
51 | /** | ||
52 | * Marks that the query definition is being initialized | ||
53 | * @since 1.4 | ||
54 | */ | ||
55 | INITIALIZING, | ||
56 | /** | ||
57 | * The query definition was successfully initialized | ||
58 | */ | ||
59 | OK, | ||
60 | /** | ||
61 | * The query definition was initialized, but some issues were present | ||
62 | */ | ||
63 | WARNING, | ||
64 | /** | ||
65 | * The query definition was not successfully initialized because of an error | ||
66 | */ | ||
67 | ERROR | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Returns all bodies associated with the query in their canonical form. If called multiple times, the same set with | ||
72 | * the same contents will be returned. | ||
73 | * | ||
74 | */ | ||
75 | PDisjunction getDisjunctBodies(); | ||
76 | |||
77 | /** | ||
78 | * Returns all queries directly referred in the constraints. They are all required to evaluate this query | ||
79 | * | ||
80 | * @return a non-null, but possibly empty list of query definitions | ||
81 | */ | ||
82 | Set<PQuery> getDirectReferredQueries(); | ||
83 | |||
84 | /** | ||
85 | * Returns all queries required to evaluate this query (transitively). | ||
86 | * | ||
87 | * @return a non-null, but possibly empty list of query definitions | ||
88 | */ | ||
89 | Set<PQuery> getAllReferredQueries(); | ||
90 | |||
91 | /** | ||
92 | * Returns the initialization status of the definition | ||
93 | * | ||
94 | */ | ||
95 | PQueryStatus getStatus(); | ||
96 | |||
97 | /** | ||
98 | * Returns a list describing the problems that were found in this query. | ||
99 | * | ||
100 | * <p> TODO: formulate invariant connecting {@link #getPProblems()} and {@link #getStatus()}. | ||
101 | * | ||
102 | * @return a non-null, but possibly empty list of problems | ||
103 | */ | ||
104 | List<PProblem> getPProblems(); | ||
105 | |||
106 | /** | ||
107 | * Before a modification operation is executed, a mutability check is performed (via the {@link #getStatus()} | ||
108 | * implementation, and in case of problems an {@link IllegalStateException} is thrown. | ||
109 | */ | ||
110 | void checkMutability(); | ||
111 | |||
112 | /** | ||
113 | * An option to check mutability of the query. It can be used to avoid getting an {@link IllegalStateException} by | ||
114 | * the execution of {@link #checkMutability()}. | ||
115 | * | ||
116 | * @return true if the query specification is still editable | ||
117 | */ | ||
118 | boolean isMutable(); | ||
119 | |||
120 | /** | ||
121 | * Optional hints regarding the query evaluation strategy, to be interpreted by the query engine. | ||
122 | * <p> To ensure the possibility of external overrides, | ||
123 | * the evaluation engine should not directly consult this field, | ||
124 | * but use an {@link IQueryBackendHintProvider} instead. | ||
125 | */ | ||
126 | public QueryEvaluationHint getEvaluationHints(); | ||
127 | |||
128 | |||
129 | /** | ||
130 | * Type information, expressed on query parameters, that all matches of the query are guaranteed to respect. | ||
131 | * <p> At the very minimum, this should include the declared types of the parameters. | ||
132 | * <p> The type judgement tuples shall contain the <i>parameter index</i>, NOT the {@link PParameter} object. | ||
133 | * | ||
134 | * @return a non-null set of type judgements that the query guarantees for its matches | ||
135 | */ | ||
136 | public Set<TypeJudgement> getTypeGuarantees(); | ||
137 | |||
138 | /** | ||
139 | * If the query definition is uninitialized, initializes it. | ||
140 | * @throws ViatraQueryRuntimeException if initialization of query specification fails | ||
141 | */ | ||
142 | public abstract void ensureInitialized(); | ||
143 | |||
144 | /** | ||
145 | * Returns the end-user query specification API objects that wrap this query. | ||
146 | * | ||
147 | * <p> Intended for traceability and debug purposes, not part of normal operation. | ||
148 | * Returned list is intended to be appended during query specification construction time. | ||
149 | * | ||
150 | * @return a non-null, but possibly empty list of query specification objects; | ||
151 | */ | ||
152 | List<Object> publishedAs(); | ||
153 | |||
154 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java new file mode 100644 index 00000000..f3671934 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java | |||
@@ -0,0 +1,101 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.queries; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Optional; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; | ||
15 | |||
16 | /** | ||
17 | * Represents header information (metainfo) about a query. | ||
18 | * <p> To be implemented both by IQuerySpecifications intended for end users, | ||
19 | * and the internal query representation {@link PQuery}. | ||
20 | * | ||
21 | * | ||
22 | * @author Bergmann Gabor | ||
23 | * @since 0.9 | ||
24 | */ | ||
25 | public interface PQueryHeader { | ||
26 | |||
27 | /** | ||
28 | * Identifies the pattern for which matchers can be instantiated. | ||
29 | */ | ||
30 | public String getFullyQualifiedName(); | ||
31 | |||
32 | /** | ||
33 | * Return the list of parameter names | ||
34 | * | ||
35 | * @return a non-null, but possibly empty list of parameter names | ||
36 | */ | ||
37 | public List<String> getParameterNames(); | ||
38 | |||
39 | /** | ||
40 | * Returns a list of parameter descriptions | ||
41 | * | ||
42 | * @return a non-null, but possibly empty list of parameter descriptions | ||
43 | */ | ||
44 | public List<PParameter> getParameters(); | ||
45 | |||
46 | /** | ||
47 | * Returns the index of a named parameter | ||
48 | * | ||
49 | * @param parameterName | ||
50 | * @return the index, or null of no such parameter is available | ||
51 | */ | ||
52 | public Integer getPositionOfParameter(String parameterName); | ||
53 | |||
54 | /** | ||
55 | * Returns a parameter by name if exists | ||
56 | * @since 2.1 | ||
57 | */ | ||
58 | default Optional<PParameter> getParameter(String parameterName) { | ||
59 | return Optional.ofNullable(getPositionOfParameter(parameterName)) | ||
60 | .map(getParameters()::get); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * Returns the list of annotations specified for this query | ||
65 | * | ||
66 | * @return a non-null, but possibly empty list of annotations | ||
67 | */ | ||
68 | public List<PAnnotation> getAllAnnotations(); | ||
69 | |||
70 | /** | ||
71 | * Returns the list of annotations with a specified name | ||
72 | * | ||
73 | * @param annotationName | ||
74 | * @return a non-null, but possibly empty list of annotations | ||
75 | */ | ||
76 | public List<PAnnotation> getAnnotationsByName(String annotationName); | ||
77 | |||
78 | /** | ||
79 | * Returns the first annotation with a specified name | ||
80 | * | ||
81 | * @since 2.0 | ||
82 | */ | ||
83 | public Optional<PAnnotation> getFirstAnnotationByName(String annotationName); | ||
84 | |||
85 | /** | ||
86 | * Returns the visibility information about the query. | ||
87 | * @since 2.0 | ||
88 | */ | ||
89 | public PVisibility getVisibility(); | ||
90 | |||
91 | /** | ||
92 | * Returns the non-qualified name of the query. By default this means returning the qualified name after the last | ||
93 | * '.' character. | ||
94 | * | ||
95 | * @since 2.0 | ||
96 | */ | ||
97 | public default String getSimpleName() { | ||
98 | return PQueries.calculateSimpleName(getFullyQualifiedName()); | ||
99 | } | ||
100 | |||
101 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java new file mode 100644 index 00000000..7cb312bd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.queries; | ||
10 | |||
11 | /** | ||
12 | * @author Zoltan Ujhelyi | ||
13 | * @since 2.0 | ||
14 | * | ||
15 | */ | ||
16 | public enum PVisibility { | ||
17 | |||
18 | /** | ||
19 | * A public (default) visibility means a pattern can be called at any time. | ||
20 | */ | ||
21 | PUBLIC, | ||
22 | /** | ||
23 | * A private query is not expected to be called directly, only by a different query matcher. | ||
24 | */ | ||
25 | PRIVATE, | ||
26 | /** | ||
27 | * A query that is only used inside a single caller query and is not visible outside its container query. Such | ||
28 | * patterns must also fulfill the following additional constraints: | ||
29 | * | ||
30 | * <ul> | ||
31 | * <li>An embedded query must have only a single body.</li> | ||
32 | * <li>An embedded query must not be recursice.</li> | ||
33 | * </ul> | ||
34 | */ | ||
35 | EMBEDDED | ||
36 | |||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java new file mode 100644 index 00000000..470d7287 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java | |||
@@ -0,0 +1,35 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Bergmann Gabor, 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.queries; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
12 | |||
13 | /** | ||
14 | * Represent an exception that occurred while initializing the specification of a query. | ||
15 | * @author Bergmann Gabor | ||
16 | * @since 0.9 | ||
17 | * | ||
18 | */ | ||
19 | public class QueryInitializationException extends QueryProcessingException { | ||
20 | |||
21 | public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription, | ||
22 | Throwable cause) { | ||
23 | super(message, context, shortMessage, patternDescription, cause); | ||
24 | } | ||
25 | |||
26 | public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription) { | ||
27 | super(message, context, shortMessage, patternDescription); | ||
28 | } | ||
29 | |||
30 | private static final long serialVersionUID = 9106033062252951489L; | ||
31 | |||
32 | |||
33 | |||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java new file mode 100644 index 00000000..276b2b42 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java | |||
@@ -0,0 +1,53 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.Objects; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
15 | |||
16 | /** | ||
17 | * @since 1.6 | ||
18 | * | ||
19 | */ | ||
20 | public class AbstractRewriterTraceSource { | ||
21 | |||
22 | private IRewriterTraceCollector traceCollector = NopTraceCollector.INSTANCE; | ||
23 | |||
24 | public void setTraceCollector(IRewriterTraceCollector traceCollector) { | ||
25 | this.traceCollector = Objects.requireNonNull(traceCollector); | ||
26 | } | ||
27 | |||
28 | public IPTraceableTraceProvider getTraces() { | ||
29 | return traceCollector; | ||
30 | } | ||
31 | |||
32 | protected IRewriterTraceCollector getTraceCollector() { | ||
33 | return traceCollector; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Mark the given derivative to be originated from the given original constraint. | ||
38 | * @since 1.6 | ||
39 | */ | ||
40 | protected void addTrace(PTraceable original, PTraceable derivative){ | ||
41 | traceCollector.addTrace(original, derivative); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Indicate that the given derivative is removed from the resulting query, thus its trace | ||
46 | * information should be removed also. | ||
47 | * @since 1.6 | ||
48 | */ | ||
49 | protected void derivativeRemoved(PConstraint derivative, IDerivativeModificationReason reason){ | ||
50 | traceCollector.derivativeRemoved(derivative, reason); | ||
51 | } | ||
52 | |||
53 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java new file mode 100644 index 00000000..237a762d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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 | /** | ||
12 | * Common reasons for removing constraint through rewriters | ||
13 | * | ||
14 | * @noreference This enum is not intended to be referenced by clients. | ||
15 | */ | ||
16 | public enum ConstraintRemovalReason implements IDerivativeModificationReason { | ||
17 | |||
18 | MOOT_EQUALITY, | ||
19 | WEAK_INEQUALITY_SELF_LOOP, | ||
20 | TYPE_SUBSUMED, | ||
21 | DUPLICATE | ||
22 | |||
23 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java new file mode 100644 index 00000000..3b5d7390 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
11 | |||
12 | /** | ||
13 | * @author Marton Bur | ||
14 | * | ||
15 | */ | ||
16 | public class DefaultFlattenCallPredicate implements IFlattenCallPredicate { | ||
17 | |||
18 | @Override | ||
19 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
20 | return true; | ||
21 | } | ||
22 | |||
23 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java new file mode 100644 index 00000000..06b8d372 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java | |||
@@ -0,0 +1,129 @@ | |||
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.HashMap; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; | ||
23 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
24 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
28 | |||
29 | /** | ||
30 | * This rewriter class can add new equality constraints to the copied body | ||
31 | * | ||
32 | * @author Marton Bur | ||
33 | * | ||
34 | */ | ||
35 | class FlattenerCopier extends PBodyCopier { | ||
36 | |||
37 | private final Map<PositivePatternCall, CallInformation> calls; | ||
38 | |||
39 | private static class CallInformation { | ||
40 | final PBody body; | ||
41 | final Map<PVariable, PVariable> variableMapping; | ||
42 | |||
43 | private CallInformation(PBody body) { | ||
44 | this.body = body; | ||
45 | this.variableMapping = new HashMap<>(); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | public FlattenerCopier(PQuery query, Map<PositivePatternCall, PBody> callsToFlatten) { | ||
50 | super(query); | ||
51 | this.calls = callsToFlatten.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> new CallInformation(entry.getValue()))); | ||
52 | } | ||
53 | |||
54 | protected void copyVariable(PositivePatternCall contextPatternCall, PVariable variable, String newName) { | ||
55 | PVariable newPVariable = body.getOrCreateVariableByName(newName); | ||
56 | calls.get(contextPatternCall).variableMapping.put(variable, newPVariable); | ||
57 | variableMapping.put(variable, newPVariable); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Merge all variables and constraints from the body called through the given pattern call to a target body. If | ||
62 | * multiple bodies are merged into a single one, use the renamer and filter options to avoid collisions. | ||
63 | * | ||
64 | * @param sourceBody | ||
65 | * @param namingTool | ||
66 | * @param filter | ||
67 | */ | ||
68 | public void mergeBody(PositivePatternCall contextPatternCall, IVariableRenamer namingTool, | ||
69 | IConstraintFilter filter) { | ||
70 | |||
71 | PBody sourceBody = calls.get(contextPatternCall).body; | ||
72 | |||
73 | // Copy variables | ||
74 | Set<PVariable> allVariables = sourceBody.getAllVariables(); | ||
75 | for (PVariable pVariable : allVariables) { | ||
76 | if (pVariable.isUnique()) { | ||
77 | copyVariable(contextPatternCall, pVariable, | ||
78 | namingTool.createVariableName(pVariable, sourceBody.getPattern())); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | // Copy constraints which are not filtered | ||
83 | Set<PConstraint> constraints = sourceBody.getConstraints(); | ||
84 | for (PConstraint pConstraint : constraints) { | ||
85 | if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { | ||
86 | copyConstraint(pConstraint); | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { | ||
93 | |||
94 | if (!calls.containsKey(positivePatternCall)) { | ||
95 | // If the call was not flattened, copy the constraint | ||
96 | super.copyPositivePatternCallConstraint(positivePatternCall); | ||
97 | } else { | ||
98 | PBody calledBody = Objects.requireNonNull(calls.get(positivePatternCall).body); | ||
99 | Preconditions.checkArgument(positivePatternCall.getReferredQuery().equals(calledBody.getPattern())); | ||
100 | |||
101 | List<PVariable> symbolicParameters = calledBody.getSymbolicParameterVariables(); | ||
102 | Object[] elements = positivePatternCall.getVariablesTuple().getElements(); | ||
103 | for (int i = 0; i < elements.length; i++) { | ||
104 | // Create equality constraints between the caller PositivePatternCall and the corresponding body | ||
105 | // parameter variables | ||
106 | createEqualityConstraint((PVariable) elements[i], symbolicParameters.get(i), positivePatternCall); | ||
107 | } | ||
108 | |||
109 | } | ||
110 | } | ||
111 | |||
112 | private void createEqualityConstraint(PVariable pVariable1, PVariable pVariable2, | ||
113 | PositivePatternCall contextPatternCall) { | ||
114 | PVariable who = variableMapping.get(pVariable1); | ||
115 | PVariable withWhom = calls.get(contextPatternCall).variableMapping.get(pVariable2); | ||
116 | addTrace(contextPatternCall, new Equality(body, who, withWhom)); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | protected void copyExpressionEvaluationConstraint(final ExpressionEvaluation expressionEvaluation) { | ||
121 | Map<PVariable, PVariable> variableMapping = this.variableMapping.entrySet().stream() | ||
122 | .filter(input -> expressionEvaluation.getPSystem().getAllVariables().contains(input.getKey())) | ||
123 | .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); | ||
124 | |||
125 | PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); | ||
126 | addTrace(expressionEvaluation, new ExpressionEvaluation(body, new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), mappedOutputVariable, expressionEvaluation.isUnwinding())); | ||
127 | } | ||
128 | |||
129 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java new file mode 100644 index 00000000..518b9c64 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, 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 tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
13 | |||
14 | /** | ||
15 | * Helper interface to exclude constraints from PBody copy processes | ||
16 | * | ||
17 | * @author Marton Bur | ||
18 | * | ||
19 | */ | ||
20 | public interface IConstraintFilter { | ||
21 | /** | ||
22 | * Returns true, if the given constraint should be filtered (thus should not be copied) | ||
23 | * | ||
24 | * @param constraint | ||
25 | * to check | ||
26 | * @return true, if the constraint should be filtered | ||
27 | */ | ||
28 | boolean filter(PConstraint constraint); | ||
29 | |||
30 | public static class ExportedParameterFilter implements IConstraintFilter { | ||
31 | |||
32 | @Override | ||
33 | public boolean filter(PConstraint constraint) { | ||
34 | return constraint instanceof ExportedParameter; | ||
35 | } | ||
36 | |||
37 | } | ||
38 | |||
39 | public static class AllowAllFilter implements IConstraintFilter { | ||
40 | |||
41 | @Override | ||
42 | public boolean filter(PConstraint constraint) { | ||
43 | // Nothing is filtered | ||
44 | return false; | ||
45 | } | ||
46 | |||
47 | } | ||
48 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java new file mode 100644 index 00000000..dbd6a78d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java | |||
@@ -0,0 +1,19 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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 | /** | ||
12 | * This is a role indication interface, implementations may provide a reason about | ||
13 | * why a modification is made during PQuery normalization. | ||
14 | * @since 1.6 | ||
15 | * | ||
16 | */ | ||
17 | public interface IDerivativeModificationReason { | ||
18 | |||
19 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java new file mode 100644 index 00000000..7e224e98 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java | |||
@@ -0,0 +1,50 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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 tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
12 | |||
13 | |||
14 | /** | ||
15 | * Interface used by the PQueryFlattener to decide which positive pattern calls to flatten | ||
16 | * | ||
17 | * @author Marton Bur | ||
18 | * | ||
19 | */ | ||
20 | public interface IFlattenCallPredicate { | ||
21 | |||
22 | /** | ||
23 | * Decides whether the called query by the pattern call should be flattened into the caller or not. | ||
24 | * | ||
25 | * @param positivePatternCall | ||
26 | * the pattern call | ||
27 | * @return true if the call should be flattened | ||
28 | */ | ||
29 | boolean shouldFlatten(PositivePatternCall positivePatternCall); | ||
30 | |||
31 | /** | ||
32 | * Flattens only if all operand predicates vote for flattening. | ||
33 | * @author Gabor Bergmann | ||
34 | * @since 2.1 | ||
35 | */ | ||
36 | public static class And implements IFlattenCallPredicate { | ||
37 | private IFlattenCallPredicate[] operands; | ||
38 | public And(IFlattenCallPredicate... operands) { | ||
39 | this.operands = operands; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
44 | for (IFlattenCallPredicate operand : operands) { | ||
45 | if (!operand.shouldFlatten(positivePatternCall)) return false; | ||
46 | } | ||
47 | return true; | ||
48 | } | ||
49 | } | ||
50 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java new file mode 100644 index 00000000..84da4d1b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * This interface provides methods to trace the {@link PTraceable}s of a transformed {@link PQuery} produced by | ||
18 | * a {@link PDisjunctionRewriter}. In case the associated rewriter is a composite (a.k.a. {@link PDisjunctionRewriterCacher}), | ||
19 | * this trace provider handles traces end-to-end, hiding all the intermediate transformation steps. | ||
20 | * | ||
21 | * @since 1.6 | ||
22 | * @noimplement This interface is not intended to be implemented by clients. | ||
23 | */ | ||
24 | public interface IPTraceableTraceProvider { | ||
25 | |||
26 | /** | ||
27 | * Find and return the canonical {@link PTraceable}s in the original query which are the sources of the given derivative | ||
28 | * {@link PTraceable} according to the transformation. | ||
29 | * | ||
30 | * @param derivative a {@link PTraceable} which is contained by the {@link PQuery} produced by the associated rewriter | ||
31 | * @since 2.0 | ||
32 | */ | ||
33 | public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative); | ||
34 | |||
35 | /** | ||
36 | * Find and return the {@link PTraceable}s in the rewritten query which are the destinations of the given source | ||
37 | * {@link PTraceable} according to the transformation. | ||
38 | * | ||
39 | * @param source a {@link PTraceable} which is contained by a {@link PQuery} before rewriting | ||
40 | * @since 2.0 | ||
41 | */ | ||
42 | public Stream<PTraceable> getRewrittenTraceables(PTraceable source); | ||
43 | |||
44 | /** | ||
45 | * Returns whether the given traceable element has been removed by every rewriter for a reason. | ||
46 | */ | ||
47 | public boolean isRemoved(PTraceable traceable); | ||
48 | |||
49 | /** | ||
50 | * Returns the reasons for which the traceable element has been removed by the rewriters. | ||
51 | * @return the reasons of removal during rewriting | ||
52 | * @since 2.0 | ||
53 | */ | ||
54 | public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable); | ||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java new file mode 100644 index 00000000..70771ea7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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 tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
12 | |||
13 | /** | ||
14 | * This is the internal API of {@link IPTraceableTraceProvider} expected to be used by | ||
15 | * copier and rewriter implementations. | ||
16 | * | ||
17 | * @since 1.6 | ||
18 | * @noreference This interface is not intended to be referenced by clients. | ||
19 | */ | ||
20 | public interface IRewriterTraceCollector extends IPTraceableTraceProvider { | ||
21 | |||
22 | /** | ||
23 | * Mark the given derivative to be originated from the given original constraint. | ||
24 | */ | ||
25 | public void addTrace(PTraceable origin, PTraceable derivative); | ||
26 | |||
27 | /** | ||
28 | * Indicate that the given derivative is removed from the resulting query, thus its trace | ||
29 | * information should be removed also. | ||
30 | */ | ||
31 | public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason); | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java new file mode 100644 index 00000000..ce446e0d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java | |||
@@ -0,0 +1,59 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, 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 tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * Helper interface to ease the naming of the new variables during flattening | ||
16 | * | ||
17 | * @author Marton Bur | ||
18 | * | ||
19 | */ | ||
20 | public interface IVariableRenamer { | ||
21 | /** | ||
22 | * Creates a variable name based on a given variable and a given query. It only creates a String, doesn't set | ||
23 | * anything. | ||
24 | * | ||
25 | * @param pVariable | ||
26 | * @param query | ||
27 | * @return the new variable name as a String | ||
28 | */ | ||
29 | String createVariableName(PVariable pVariable, PQuery query); | ||
30 | |||
31 | public class SameName implements IVariableRenamer { | ||
32 | @Override | ||
33 | public String createVariableName(PVariable pVariable, PQuery query) { | ||
34 | return pVariable.getName(); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | public class HierarchicalName implements IVariableRenamer { | ||
39 | |||
40 | private int callCount; | ||
41 | |||
42 | public void setCallCount(int callCount) { | ||
43 | this.callCount = callCount; | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public String createVariableName(PVariable pVariable, PQuery query) { | ||
48 | // make sure to keep the "_" prefix before anonymous variables | ||
49 | String newVarName = getShortName(query) + "<" + callCount + ">" + "_" + pVariable.getName(); | ||
50 | return pVariable.getName().startsWith("_") ? "_" + newVarName : newVarName ; | ||
51 | } | ||
52 | |||
53 | private String getShortName(PQuery query) { | ||
54 | String fullyQualifiedName = query.getFullyQualifiedName(); | ||
55 | int beginIndex = fullyQualifiedName.lastIndexOf('.') + 1; | ||
56 | return fullyQualifiedName.substring(beginIndex); | ||
57 | } | ||
58 | } | ||
59 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java new file mode 100644 index 00000000..7429fc60 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java | |||
@@ -0,0 +1,135 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.LinkedList; | ||
15 | import java.util.Map; | ||
16 | import java.util.Queue; | ||
17 | import java.util.Set; | ||
18 | import java.util.function.Predicate; | ||
19 | import java.util.stream.Stream; | ||
20 | |||
21 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
26 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
27 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
28 | |||
29 | /** | ||
30 | * Multimap-based implementation to contain and query traces | ||
31 | * | ||
32 | * @since 1.6 | ||
33 | * | ||
34 | */ | ||
35 | public class MappingTraceCollector implements IRewriterTraceCollector { | ||
36 | |||
37 | /** | ||
38 | * Traces from derivative to original | ||
39 | */ | ||
40 | private final IMultiLookup<PTraceable, PTraceable> traces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
41 | |||
42 | /** | ||
43 | * Traces from original to derivative | ||
44 | */ | ||
45 | private final IMultiLookup<PTraceable, PTraceable> inverseTraces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
46 | |||
47 | /** | ||
48 | * Reasons for removing {@link PTraceable}s | ||
49 | */ | ||
50 | private final Map<PTraceable, IDerivativeModificationReason> removals = new HashMap<>(); | ||
51 | |||
52 | /** | ||
53 | * Decides whether {@link PTraceable} is removed | ||
54 | */ | ||
55 | private final Predicate<PTraceable> removed = removals::containsKey; | ||
56 | |||
57 | /** | ||
58 | * @since 2.0 | ||
59 | */ | ||
60 | @Override | ||
61 | public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative) { | ||
62 | return findTraceEnds(derivative, traces).stream(); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * @since 2.0 | ||
67 | */ | ||
68 | @Override | ||
69 | public Stream<PTraceable> getRewrittenTraceables(PTraceable source) { | ||
70 | return findTraceEnds(source, inverseTraces).stream(); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Returns the end of trace chains starting from the given {@link PTraceable} along the given trace edges. | ||
75 | */ | ||
76 | private Set<PTraceable> findTraceEnds(PTraceable traceable, IMultiLookup<PTraceable, PTraceable> traceRecords) { | ||
77 | if (traceable instanceof PQuery) { // PQueries are preserved | ||
78 | return Collections.singleton(traceable); | ||
79 | } | ||
80 | Set<PTraceable> visited = new HashSet<>(); | ||
81 | Set<PTraceable> result = new HashSet<>(); | ||
82 | Queue<PTraceable> queue = new LinkedList<>(); | ||
83 | queue.add(traceable); | ||
84 | while(!queue.isEmpty()){ | ||
85 | PTraceable aDerivative = queue.poll(); | ||
86 | // Track visited elements to avoid infinite loop via directed cycles in traces | ||
87 | visited.add(aDerivative); | ||
88 | IMemoryView<PTraceable> nextOrigins = traceRecords.lookup(aDerivative); | ||
89 | if (nextOrigins == null){ | ||
90 | // End of trace chain | ||
91 | result.add(aDerivative); | ||
92 | } else { | ||
93 | // Follow traces | ||
94 | for(PTraceable nextOrigin : nextOrigins){ | ||
95 | if (!visited.contains(nextOrigin)){ | ||
96 | queue.add(nextOrigin); | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | return result; | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public void addTrace(PTraceable original, PTraceable derivative){ | ||
106 | traces.addPairOrNop(derivative, original); | ||
107 | inverseTraces.addPairOrNop(original, derivative); | ||
108 | // Even if this element was marked as removed earlier, now we replace it with another constraint! | ||
109 | removals.remove(original); | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason){ | ||
114 | Preconditions.checkState(!removals.containsKey(derivative), "Traceable %s removed multiple times", derivative); | ||
115 | // XXX the derivative must not be removed from the trace chain, as some rewriters, e.g. the normalizer keeps trace links to deleted elements | ||
116 | if (!inverseTraces.lookupExists(derivative)) { | ||
117 | // If there already exists a trace link, this removal means an update | ||
118 | removals.put(derivative, reason); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | public boolean isRemoved(PTraceable traceable) { | ||
124 | return getRewrittenTraceables(traceable).allMatch(removed); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * @since 2.0 | ||
129 | */ | ||
130 | @Override | ||
131 | public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable) { | ||
132 | return getRewrittenTraceables(traceable).filter(removed).map(removals::get); | ||
133 | } | ||
134 | |||
135 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java new file mode 100644 index 00000000..96c0b205 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. | ||
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 tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
12 | |||
13 | /** | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public class NeverFlattenCallPredicate implements IFlattenCallPredicate { | ||
19 | |||
20 | |||
21 | @Override | ||
22 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
23 | return false; | ||
24 | } | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java new file mode 100644 index 00000000..15cf577e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs | ||
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.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; | ||
14 | |||
15 | /** | ||
16 | * This implementation does not store any traces and scales to NOP for every traceability feature. | ||
17 | * @since 1.6 | ||
18 | * | ||
19 | */ | ||
20 | public class NopTraceCollector implements IRewriterTraceCollector { | ||
21 | |||
22 | public static final IRewriterTraceCollector INSTANCE = new NopTraceCollector(); | ||
23 | |||
24 | private NopTraceCollector() { | ||
25 | // Private constructor to force using the common instance | ||
26 | } | ||
27 | |||
28 | /** | ||
29 | * @since 2.0 | ||
30 | */ | ||
31 | @Override | ||
32 | public Stream<PTraceable> getCanonicalTraceables(PTraceable derivative) { | ||
33 | return Stream.empty(); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * @since 2.0 | ||
38 | */ | ||
39 | @Override | ||
40 | public Stream<PTraceable> getRewrittenTraceables(PTraceable source) { | ||
41 | return Stream.empty(); | ||
42 | } | ||
43 | |||
44 | |||
45 | @Override | ||
46 | public void addTrace(PTraceable origin, PTraceable derivative) { | ||
47 | // ignored | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason) { | ||
52 | // ignored | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public boolean isRemoved(PTraceable traceable) { | ||
57 | return false; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * @since 2.0 | ||
62 | */ | ||
63 | @Override | ||
64 | public Stream<IDerivativeModificationReason> getRemovalReasons(PTraceable traceable) { | ||
65 | return Stream.empty(); | ||
66 | } | ||
67 | |||
68 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java new file mode 100644 index 00000000..e66c4eea --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java | |||
@@ -0,0 +1,306 @@ | |||
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 tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
24 | |||
25 | import java.util.*; | ||
26 | import java.util.stream.Collectors; | ||
27 | |||
28 | /** | ||
29 | * This class can create a new PBody for a PQuery. The result body contains a copy of given variables and constraints. | ||
30 | * | ||
31 | * @author Marton Bur | ||
32 | * | ||
33 | */ | ||
34 | public class PBodyCopier extends AbstractRewriterTraceSource { | ||
35 | |||
36 | /** | ||
37 | * The created body | ||
38 | */ | ||
39 | protected PBody body; | ||
40 | /** | ||
41 | * Mapping between the original and the copied variables | ||
42 | */ | ||
43 | protected Map<PVariable, PVariable> variableMapping = new HashMap<>(); | ||
44 | |||
45 | public Map<PVariable, PVariable> getVariableMapping() { | ||
46 | return variableMapping; | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * @since 1.6 | ||
51 | */ | ||
52 | public PBodyCopier(PBody body, IRewriterTraceCollector traceCollector) { | ||
53 | this.body = new PBody(body.getPattern()); | ||
54 | setTraceCollector(traceCollector); | ||
55 | |||
56 | // do the actual copying | ||
57 | mergeBody(body); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * @since 1.6 | ||
62 | */ | ||
63 | public PBodyCopier(PQuery query) { | ||
64 | this.body = new PBody(query); | ||
65 | } | ||
66 | |||
67 | public void mergeBody(PBody sourceBody) { | ||
68 | mergeBody(sourceBody, new SameName(), new AllowAllFilter()); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * Merge all variables and constraints from a source body to a target body. If multiple bodies are merged into a | ||
73 | * single one, use the renamer and filter options to avoid collisions. | ||
74 | */ | ||
75 | public void mergeBody(PBody sourceBody, IVariableRenamer namingTool, IConstraintFilter filter) { | ||
76 | |||
77 | // Copy variables | ||
78 | Set<PVariable> allVariables = sourceBody.getAllVariables(); | ||
79 | for (PVariable pVariable : allVariables) { | ||
80 | if (pVariable.isUnique()) { | ||
81 | copyVariable(pVariable, namingTool.createVariableName(pVariable, sourceBody.getPattern())); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | // Copy exported parameters | ||
86 | this.body.setSymbolicParameters(sourceBody.getSymbolicParameters().stream() | ||
87 | .map(this::copyExportedParameterConstraint).collect(Collectors.toList())); | ||
88 | |||
89 | // Copy constraints which are not filtered | ||
90 | Set<PConstraint> constraints = sourceBody.getConstraints(); | ||
91 | for (PConstraint pConstraint : constraints) { | ||
92 | if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { | ||
93 | copyConstraint(pConstraint); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | // Add trace between original and copied body | ||
98 | addTrace(sourceBody, body); | ||
99 | } | ||
100 | |||
101 | protected void copyVariable(PVariable variable, String newName) { | ||
102 | PVariable newPVariable = body.getOrCreateVariableByName(newName); | ||
103 | variableMapping.put(variable, newPVariable); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * Returns the body with the copied variables and constraints. The returned body is still uninitialized. | ||
108 | */ | ||
109 | public PBody getCopiedBody() { | ||
110 | return body; | ||
111 | } | ||
112 | |||
113 | protected void copyConstraint(PConstraint constraint) { | ||
114 | if (constraint instanceof ExportedParameter) { | ||
115 | copyExportedParameterConstraint((ExportedParameter) constraint); | ||
116 | } else if (constraint instanceof Equality) { | ||
117 | copyEqualityConstraint((Equality) constraint); | ||
118 | } else if (constraint instanceof Inequality) { | ||
119 | copyInequalityConstraint((Inequality) constraint); | ||
120 | } else if (constraint instanceof TypeConstraint) { | ||
121 | copyTypeConstraint((TypeConstraint) constraint); | ||
122 | } else if (constraint instanceof TypeFilterConstraint) { | ||
123 | copyTypeFilterConstraint((TypeFilterConstraint) constraint); | ||
124 | } else if (constraint instanceof ConstantValue) { | ||
125 | copyConstantValueConstraint((ConstantValue) constraint); | ||
126 | } else if (constraint instanceof PositivePatternCall) { | ||
127 | copyPositivePatternCallConstraint((PositivePatternCall) constraint); | ||
128 | } else if (constraint instanceof NegativePatternCall) { | ||
129 | copyNegativePatternCallConstraint((NegativePatternCall) constraint); | ||
130 | } else if (constraint instanceof BinaryTransitiveClosure) { | ||
131 | copyBinaryTransitiveClosureConstraint((BinaryTransitiveClosure) constraint); | ||
132 | } else if (constraint instanceof RepresentativeElectionConstraint) { | ||
133 | copyRepresentativeElectionConstraint((RepresentativeElectionConstraint) constraint); | ||
134 | } else if (constraint instanceof RelationEvaluation) { | ||
135 | copyRelationEvaluationConstraint((RelationEvaluation) constraint); | ||
136 | } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { | ||
137 | copyBinaryReflexiveTransitiveClosureConstraint((BinaryReflexiveTransitiveClosure) constraint); | ||
138 | } else if (constraint instanceof PatternMatchCounter) { | ||
139 | copyPatternMatchCounterConstraint((PatternMatchCounter) constraint); | ||
140 | } else if (constraint instanceof AggregatorConstraint) { | ||
141 | copyAggregatorConstraint((AggregatorConstraint) constraint); | ||
142 | } else if (constraint instanceof ExpressionEvaluation) { | ||
143 | copyExpressionEvaluationConstraint((ExpressionEvaluation) constraint); | ||
144 | } else { | ||
145 | throw new QueryProcessingException("Unknown PConstraint {0} encountered while copying PBody", | ||
146 | new String[] { constraint.getClass().getName() }, "Unknown PConstraint", body.getPattern()); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | protected ExportedParameter copyExportedParameterConstraint(ExportedParameter exportedParameter) { | ||
151 | PVariable mappedPVariable = variableMapping.get(exportedParameter.getParameterVariable()); | ||
152 | PParameter parameter = exportedParameter.getPatternParameter(); | ||
153 | ExportedParameter newExportedParameter; | ||
154 | newExportedParameter = new ExportedParameter(body, mappedPVariable, parameter); | ||
155 | body.getSymbolicParameters().add(newExportedParameter); | ||
156 | addTrace(exportedParameter, newExportedParameter); | ||
157 | return newExportedParameter; | ||
158 | } | ||
159 | |||
160 | protected void copyEqualityConstraint(Equality equality) { | ||
161 | PVariable who = equality.getWho(); | ||
162 | PVariable withWhom = equality.getWithWhom(); | ||
163 | addTrace(equality, new Equality(body, variableMapping.get(who), variableMapping.get(withWhom))); | ||
164 | } | ||
165 | |||
166 | protected void copyInequalityConstraint(Inequality inequality) { | ||
167 | PVariable who = inequality.getWho(); | ||
168 | PVariable withWhom = inequality.getWithWhom(); | ||
169 | addTrace(inequality, new Inequality(body, variableMapping.get(who), variableMapping.get(withWhom))); | ||
170 | } | ||
171 | |||
172 | protected void copyTypeConstraint(TypeConstraint typeConstraint) { | ||
173 | PVariable[] mappedVariables = extractMappedVariables(typeConstraint); | ||
174 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
175 | addTrace(typeConstraint, new TypeConstraint(body, variablesTuple, typeConstraint.getSupplierKey())); | ||
176 | } | ||
177 | |||
178 | protected void copyTypeFilterConstraint(TypeFilterConstraint typeConstraint) { | ||
179 | PVariable[] mappedVariables = extractMappedVariables(typeConstraint); | ||
180 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
181 | addTrace(typeConstraint, new TypeFilterConstraint(body, variablesTuple, typeConstraint.getInputKey())); | ||
182 | } | ||
183 | |||
184 | protected void copyConstantValueConstraint(ConstantValue constantValue) { | ||
185 | PVariable pVariable = (PVariable) constantValue.getVariablesTuple().getElements()[0]; | ||
186 | addTrace(constantValue, | ||
187 | new ConstantValue(body, variableMapping.get(pVariable), constantValue.getSupplierKey())); | ||
188 | } | ||
189 | |||
190 | protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { | ||
191 | PVariable[] mappedVariables = extractMappedVariables(positivePatternCall); | ||
192 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
193 | addTrace(positivePatternCall, | ||
194 | new PositivePatternCall(body, variablesTuple, positivePatternCall.getReferredQuery())); | ||
195 | } | ||
196 | |||
197 | protected void copyNegativePatternCallConstraint(NegativePatternCall negativePatternCall) { | ||
198 | PVariable[] mappedVariables = extractMappedVariables(negativePatternCall); | ||
199 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
200 | addTrace(negativePatternCall, | ||
201 | new NegativePatternCall(body, variablesTuple, negativePatternCall.getReferredQuery())); | ||
202 | } | ||
203 | |||
204 | protected void copyBinaryTransitiveClosureConstraint(BinaryTransitiveClosure binaryTransitiveClosure) { | ||
205 | PVariable[] mappedVariables = extractMappedVariables(binaryTransitiveClosure); | ||
206 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
207 | addTrace(binaryTransitiveClosure, | ||
208 | new BinaryTransitiveClosure(body, variablesTuple, binaryTransitiveClosure.getReferredQuery())); | ||
209 | } | ||
210 | |||
211 | protected void copyRepresentativeElectionConstraint(RepresentativeElectionConstraint constraint) { | ||
212 | var mappedVariables = extractMappedVariables(constraint); | ||
213 | var variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
214 | addTrace(constraint, new RepresentativeElectionConstraint(body, variablesTuple, constraint.getReferredQuery(), | ||
215 | constraint.getConnectivity())); | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * @since 2.8 | ||
220 | */ | ||
221 | protected void copyRelationEvaluationConstraint(RelationEvaluation relationEvaluation) { | ||
222 | PVariable[] mappedVariables = extractMappedVariables(relationEvaluation); | ||
223 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
224 | addTrace(relationEvaluation, new RelationEvaluation(body, variablesTuple, relationEvaluation.getReferredQueries(), | ||
225 | relationEvaluation.getEvaluator())); | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * @since 2.0 | ||
230 | */ | ||
231 | protected void copyBinaryReflexiveTransitiveClosureConstraint( | ||
232 | BinaryReflexiveTransitiveClosure binaryReflexiveTransitiveClosure) { | ||
233 | PVariable[] mappedVariables = extractMappedVariables(binaryReflexiveTransitiveClosure); | ||
234 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
235 | addTrace(binaryReflexiveTransitiveClosure, | ||
236 | new BinaryReflexiveTransitiveClosure(body, variablesTuple, | ||
237 | binaryReflexiveTransitiveClosure.getReferredQuery(), | ||
238 | binaryReflexiveTransitiveClosure.getUniverseType())); | ||
239 | } | ||
240 | |||
241 | protected void copyPatternMatchCounterConstraint(PatternMatchCounter patternMatchCounter) { | ||
242 | PVariable[] mappedVariables = extractMappedVariables(patternMatchCounter); | ||
243 | PVariable mappedResultVariable = variableMapping.get(patternMatchCounter.getResultVariable()); | ||
244 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
245 | addTrace(patternMatchCounter, new PatternMatchCounter(body, variablesTuple, | ||
246 | patternMatchCounter.getReferredQuery(), mappedResultVariable)); | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * @since 1.4 | ||
251 | */ | ||
252 | protected void copyAggregatorConstraint(AggregatorConstraint constraint) { | ||
253 | PVariable[] mappedVariables = extractMappedVariables(constraint); | ||
254 | PVariable mappedResultVariable = variableMapping.get(constraint.getResultVariable()); | ||
255 | Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); | ||
256 | addTrace(constraint, new AggregatorConstraint(constraint.getAggregator(), body, variablesTuple, | ||
257 | constraint.getReferredQuery(), mappedResultVariable, constraint.getAggregatedColumn())); | ||
258 | } | ||
259 | |||
260 | protected void copyExpressionEvaluationConstraint(ExpressionEvaluation expressionEvaluation) { | ||
261 | PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); | ||
262 | addTrace(expressionEvaluation, new ExpressionEvaluation(body, | ||
263 | new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), | ||
264 | mappedOutputVariable, expressionEvaluation.isUnwinding())); | ||
265 | } | ||
266 | |||
267 | /** | ||
268 | * For positive pattern calls | ||
269 | * | ||
270 | * @param positivePatternCall | ||
271 | * @return the mapped variables to the pattern's parameters | ||
272 | */ | ||
273 | protected PVariable[] extractMappedVariables(EnumerablePConstraint enumerablePConstraint) { | ||
274 | Object[] pVariables = enumerablePConstraint.getVariablesTuple().getElements(); | ||
275 | return mapVariableList(pVariables); | ||
276 | } | ||
277 | |||
278 | /** | ||
279 | * For negative and count pattern calls. | ||
280 | * | ||
281 | * @param patternMatchCounter | ||
282 | * @return the mapped variables to the pattern's parameters | ||
283 | */ | ||
284 | private PVariable[] extractMappedVariables(PatternCallBasedDeferred patternCallBasedDeferred) { | ||
285 | Object[] pVariables = patternCallBasedDeferred.getActualParametersTuple().getElements(); | ||
286 | return mapVariableList(pVariables); | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * For type filters. | ||
291 | */ | ||
292 | private PVariable[] extractMappedVariables(TypeFilterConstraint typeFilterConstraint) { | ||
293 | Object[] pVariables = typeFilterConstraint.getVariablesTuple().getElements(); | ||
294 | return mapVariableList(pVariables); | ||
295 | } | ||
296 | |||
297 | private PVariable[] mapVariableList(Object[] pVariables) { | ||
298 | List<PVariable> list = new ArrayList<PVariable>(); | ||
299 | for (int i = 0; i < pVariables.length; i++) { | ||
300 | PVariable mappedVariable = variableMapping.get(pVariables[i]); | ||
301 | list.add(mappedVariable); | ||
302 | } | ||
303 | return list.toArray(new PVariable[0]); | ||
304 | } | ||
305 | |||
306 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java new file mode 100644 index 00000000..90943129 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java | |||
@@ -0,0 +1,310 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2010 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.psystem.rewriters; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collection; | ||
14 | import java.util.Collections; | ||
15 | import java.util.Comparator; | ||
16 | import java.util.HashMap; | ||
17 | import java.util.HashSet; | ||
18 | import java.util.Iterator; | ||
19 | import java.util.LinkedHashSet; | ||
20 | import java.util.LinkedList; | ||
21 | import java.util.List; | ||
22 | import java.util.Map; | ||
23 | import java.util.Queue; | ||
24 | import java.util.Set; | ||
25 | |||
26 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
29 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; | ||
36 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; | ||
37 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
38 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
39 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
40 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
41 | |||
42 | /** | ||
43 | * A disjunction rewriter for creating a normalized form of specification, unifying variables and running basic sanity | ||
44 | * checks. This rewriter does not copy but modifies directly the original specification, requiring a mutable | ||
45 | * disjunction. | ||
46 | * | ||
47 | * @author Gabor Bergmann | ||
48 | * | ||
49 | */ | ||
50 | public class PBodyNormalizer extends PDisjunctionRewriter { | ||
51 | |||
52 | private IQueryMetaContext context; | ||
53 | |||
54 | public PBodyNormalizer(IQueryMetaContext context) { | ||
55 | this.context = context; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * Returns whether unary constraint elimination is enabled. This behavior can be customized by creating a subclass | ||
60 | * with a custom implementation. | ||
61 | * | ||
62 | * @since 1.6 | ||
63 | */ | ||
64 | protected boolean shouldCalculateImpliedTypes(PQuery query) { | ||
65 | return true; | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Returns whether 'weakened alternative' suggestions of the context shall be expanded as additional PConstraints. | ||
70 | * This behavior can be customized by creating a subclass | ||
71 | * with a custom implementation. | ||
72 | * | ||
73 | * @since 1.6 | ||
74 | */ | ||
75 | protected boolean shouldExpandWeakenedAlternatives(PQuery query) { | ||
76 | return false; | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
81 | Set<PBody> normalizedBodies = new LinkedHashSet<>(); | ||
82 | for (PBody body : disjunction.getBodies()) { | ||
83 | PBodyCopier copier = new PBodyCopier(body, getTraceCollector()); | ||
84 | PBody modifiedBody = copier.getCopiedBody(); | ||
85 | normalizeBody(modifiedBody); | ||
86 | normalizedBodies.add(modifiedBody); | ||
87 | modifiedBody.setStatus(PQueryStatus.OK); | ||
88 | } | ||
89 | return new PDisjunction(normalizedBodies); | ||
90 | } | ||
91 | |||
92 | public void setContext(IQueryMetaContext context) { | ||
93 | this.context = context; | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * Provides a normalized version of the pattern body. May return a different version than the original version if | ||
98 | * needed. | ||
99 | * | ||
100 | * @param body | ||
101 | */ | ||
102 | public PBody normalizeBody(PBody body) { | ||
103 | try { | ||
104 | return normalizeBodyInternal(body); | ||
105 | } catch (QueryProcessingException e) { | ||
106 | throw new RewriterException("Error during rewriting: {1}", new String[] { e.getMessage() }, | ||
107 | e.getShortMessage(), body.getPattern(), e); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | PBody normalizeBodyInternal(PBody body) { | ||
112 | // UNIFICATION AND WEAK INEQUALITY ELIMINATION | ||
113 | unifyVariablesAlongEqualities(body); | ||
114 | eliminateWeakInequalities(body); | ||
115 | removeMootEqualities(body); | ||
116 | |||
117 | // ADDING WEAKENED ALTERNATIVES | ||
118 | if (shouldExpandWeakenedAlternatives(body.getPattern())) { | ||
119 | expandWeakenedAlternativeConstraints(body); | ||
120 | } | ||
121 | |||
122 | // CONSTRAINT ELIMINATION WITH TYPE INFERENCE | ||
123 | if (shouldCalculateImpliedTypes(body.getPattern())) { | ||
124 | eliminateInferrableTypes(body, context); | ||
125 | } else { | ||
126 | // ELIMINATE DUPLICATE TYPE CONSTRAINTS | ||
127 | eliminateDuplicateTypeConstraints(body); | ||
128 | } | ||
129 | |||
130 | |||
131 | // PREVENTIVE CHECKS | ||
132 | checkSanity(body); | ||
133 | return body; | ||
134 | } | ||
135 | |||
136 | private void removeMootEqualities(PBody body) { | ||
137 | Set<Equality> equals = body.getConstraintsOfType(Equality.class); | ||
138 | for (Equality equality : equals) { | ||
139 | if (equality.isMoot()) { | ||
140 | equality.delete(); | ||
141 | derivativeRemoved(equality, ConstraintRemovalReason.MOOT_EQUALITY); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * Unifies allVariables along equalities so that they can be handled as one. | ||
148 | * | ||
149 | * @param body | ||
150 | */ | ||
151 | void unifyVariablesAlongEqualities(PBody body) { | ||
152 | Set<Equality> equals = body.getConstraintsOfType(Equality.class); | ||
153 | for (Equality equality : equals) { | ||
154 | if (!equality.isMoot()) { | ||
155 | equality.getWho().unifyInto(equality.getWithWhom()); | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * Eliminates weak inequalities if they are not substantiated. | ||
162 | * | ||
163 | * @param body | ||
164 | */ | ||
165 | void eliminateWeakInequalities(PBody body) { | ||
166 | for (Inequality inequality : body.getConstraintsOfType(Inequality.class)){ | ||
167 | if (inequality.isEliminable()){ | ||
168 | inequality.eliminateWeak(); | ||
169 | derivativeRemoved(inequality, ConstraintRemovalReason.WEAK_INEQUALITY_SELF_LOOP); | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * Eliminates all type constraints that are inferrable from other constraints. | ||
176 | */ | ||
177 | void eliminateInferrableTypes(final PBody body, IQueryMetaContext context) { | ||
178 | Set<TypeJudgement> subsumedByRetainedConstraints = new HashSet<TypeJudgement>(); | ||
179 | LinkedList<ITypeConstraint> allTypeConstraints = new LinkedList<ITypeConstraint>(); | ||
180 | for (PConstraint pConstraint : body.getConstraints()) { | ||
181 | if (pConstraint instanceof ITypeConstraint) { | ||
182 | allTypeConstraints.add((ITypeConstraint) pConstraint); | ||
183 | } else if (pConstraint instanceof ITypeInfoProviderConstraint) { | ||
184 | // non-type constraints are all retained | ||
185 | final Set<TypeJudgement> directJudgements = ((ITypeInfoProviderConstraint) pConstraint) | ||
186 | .getImpliedJudgements(context); | ||
187 | subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, directJudgements, | ||
188 | context); | ||
189 | } | ||
190 | } | ||
191 | Comparator<ITypeConstraint> eliminationOrder = (o1, o2) -> { | ||
192 | IInputKey type1 = o1.getEquivalentJudgement().getInputKey(); | ||
193 | IInputKey type2 = o2.getEquivalentJudgement().getInputKey(); | ||
194 | |||
195 | int result = context.getSuggestedEliminationOrdering().compare(type1, type2); | ||
196 | return (result == 0) | ||
197 | ? PConstraint.COMPARE_BY_MONOTONOUS_ID.compare(o1, o2) | ||
198 | : result; | ||
199 | }; | ||
200 | |||
201 | Collections.sort(allTypeConstraints, eliminationOrder); | ||
202 | Queue<ITypeConstraint> potentialConstraints = allTypeConstraints; // rename for better comprehension | ||
203 | |||
204 | while (!potentialConstraints.isEmpty()) { | ||
205 | ITypeConstraint candidate = potentialConstraints.poll(); | ||
206 | |||
207 | boolean isSubsumed = subsumedByRetainedConstraints.contains(candidate.getEquivalentJudgement()); | ||
208 | if (!isSubsumed) { | ||
209 | Set<TypeJudgement> typeClosure = subsumedByRetainedConstraints; | ||
210 | for (ITypeConstraint subsuming : potentialConstraints) { // the remaining ones | ||
211 | final Set<TypeJudgement> directJudgements = subsuming.getImpliedJudgements(context); | ||
212 | typeClosure = TypeHelper.typeClosure(typeClosure, directJudgements, context); | ||
213 | |||
214 | if (typeClosure.contains(candidate.getEquivalentJudgement())) { | ||
215 | isSubsumed = true; | ||
216 | break; | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | if (isSubsumed) { // eliminated | ||
221 | candidate.delete(); | ||
222 | derivativeRemoved(candidate, ConstraintRemovalReason.TYPE_SUBSUMED); | ||
223 | } else { // retained | ||
224 | subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, | ||
225 | candidate.getImpliedJudgements(context), context); | ||
226 | } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * Inserts "weakened alternative" constraints suggested by the meta context that aid in coming up with a query plan. | ||
232 | */ | ||
233 | void expandWeakenedAlternativeConstraints(PBody body) { | ||
234 | Set<TypeJudgement> allJudgements = new HashSet<TypeJudgement>(); | ||
235 | Set<TypeJudgement> newJudgementsToAdd = new HashSet<TypeJudgement>(); | ||
236 | Queue<TypeJudgement> judgementsToProcess = new LinkedList<TypeJudgement>(); | ||
237 | Map<TypeJudgement, List<PConstraint>> traceability = CollectionsFactory.createMap(); | ||
238 | |||
239 | for (ITypeConstraint typeConstraint : body.getConstraintsOfType(ITypeConstraint.class)) { | ||
240 | TypeJudgement equivalentJudgement = typeConstraint.getEquivalentJudgement(); | ||
241 | judgementsToProcess.add(equivalentJudgement); | ||
242 | allJudgements.add(equivalentJudgement); | ||
243 | traceability.computeIfAbsent(equivalentJudgement, k-> new ArrayList<>()).add(typeConstraint); | ||
244 | } | ||
245 | |||
246 | while (!judgementsToProcess.isEmpty()) { | ||
247 | TypeJudgement judgement = judgementsToProcess.poll(); | ||
248 | for (TypeJudgement alternativeJudgement : judgement.getWeakenedAlternativeJudgements(context)) { | ||
249 | if (allJudgements.add(alternativeJudgement)) { | ||
250 | newJudgementsToAdd.add(alternativeJudgement); | ||
251 | judgementsToProcess.add(alternativeJudgement); | ||
252 | traceability.merge( | ||
253 | alternativeJudgement, | ||
254 | traceability.getOrDefault(judgement, new ArrayList<>()), | ||
255 | (old,further) -> {old.addAll(further); return old;} | ||
256 | ); | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | |||
261 | for (TypeJudgement typeJudgement : newJudgementsToAdd) { | ||
262 | PConstraint newConstraint = typeJudgement.createConstraintFor(body); | ||
263 | for (PConstraint source : traceability.getOrDefault(typeJudgement, Collections.emptyList())) { | ||
264 | addTrace(source, newConstraint); | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | |||
269 | private Object getConstraintKey(PConstraint constraint) { | ||
270 | if (constraint instanceof ITypeConstraint) { | ||
271 | return ((ITypeConstraint) constraint).getEquivalentJudgement(); | ||
272 | } | ||
273 | // Do not check duplication for any other types | ||
274 | return constraint; | ||
275 | } | ||
276 | |||
277 | void eliminateDuplicateTypeConstraints(PBody body) { | ||
278 | Map<Object, PConstraint> constraints = new HashMap<>(); | ||
279 | for (PConstraint constraint : body.getConstraints()) { | ||
280 | Object key = getConstraintKey(constraint); | ||
281 | // Retain first found instance of a constraint | ||
282 | if (!constraints.containsKey(key)) { | ||
283 | constraints.put(key, constraint); | ||
284 | } | ||
285 | } | ||
286 | |||
287 | // Retain collected constraints, remove everything else | ||
288 | Iterator<PConstraint> iterator = body.getConstraints().iterator(); | ||
289 | Collection<PConstraint> toRetain = constraints.values(); | ||
290 | while(iterator.hasNext()){ | ||
291 | PConstraint next = iterator.next(); | ||
292 | if (!toRetain.contains(next)){ | ||
293 | derivativeRemoved(next, ConstraintRemovalReason.DUPLICATE); | ||
294 | iterator.remove(); | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | /** | ||
300 | * Verifies the sanity of all constraints. Should be issued as a preventive check before layouting. | ||
301 | * | ||
302 | * @param body | ||
303 | * @throws RetePatternBuildException | ||
304 | */ | ||
305 | void checkSanity(PBody body) { | ||
306 | for (PConstraint pConstraint : body.getConstraints()) | ||
307 | pConstraint.checkSanity(); | ||
308 | } | ||
309 | |||
310 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java new file mode 100644 index 00000000..c844ccf7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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 tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
12 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
13 | |||
14 | /** | ||
15 | * An abstract base class for creating alternative representations for PDisjunctions. | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * | ||
18 | */ | ||
19 | public abstract class PDisjunctionRewriter extends AbstractRewriterTraceSource{ | ||
20 | |||
21 | public abstract PDisjunction rewrite(PDisjunction disjunction); | ||
22 | |||
23 | public PDisjunction rewrite(PQuery query) { | ||
24 | return rewrite(query.getDisjunctBodies()); | ||
25 | } | ||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java new file mode 100644 index 00000000..eb5422ca --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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.ArrayList; | ||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.List; | ||
15 | import java.util.WeakHashMap; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
18 | |||
19 | /** | ||
20 | * A rewriter that stores the previously computed results of a rewriter or a rewriter chain. | ||
21 | * | ||
22 | * @author Zoltan Ujhelyi | ||
23 | * @since 1.0 | ||
24 | */ | ||
25 | public class PDisjunctionRewriterCacher extends PDisjunctionRewriter { | ||
26 | |||
27 | private final List<PDisjunctionRewriter> rewriterChain; | ||
28 | private WeakHashMap<PDisjunction, PDisjunction> cachedResults = | ||
29 | new WeakHashMap<PDisjunction, PDisjunction>(); | ||
30 | |||
31 | private void setupTraceCollectorInChain(){ | ||
32 | IRewriterTraceCollector collector = getTraceCollector(); | ||
33 | for(PDisjunctionRewriter rewriter: rewriterChain){ | ||
34 | rewriter.setTraceCollector(collector); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | public PDisjunctionRewriterCacher(PDisjunctionRewriter rewriter) { | ||
39 | rewriterChain = Collections.singletonList(rewriter); | ||
40 | } | ||
41 | |||
42 | public PDisjunctionRewriterCacher(PDisjunctionRewriter... rewriters) { | ||
43 | rewriterChain = new ArrayList<>(Arrays.asList(rewriters)); | ||
44 | } | ||
45 | |||
46 | public PDisjunctionRewriterCacher(List<PDisjunctionRewriter> rewriterChain) { | ||
47 | this.rewriterChain = new ArrayList<>(rewriterChain); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
52 | if (!cachedResults.containsKey(disjunction)) { | ||
53 | PDisjunction rewritten = disjunction; | ||
54 | setupTraceCollectorInChain(); | ||
55 | for (PDisjunctionRewriter rewriter : rewriterChain) { | ||
56 | rewritten = rewriter.rewrite(rewritten); | ||
57 | } | ||
58 | |||
59 | cachedResults.put(disjunction, rewritten); | ||
60 | } | ||
61 | return cachedResults.get(disjunction); | ||
62 | } | ||
63 | |||
64 | } | ||
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 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java new file mode 100644 index 00000000..d0fc286b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java | |||
@@ -0,0 +1,31 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, 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 tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException; | ||
12 | |||
13 | /** | ||
14 | * An exception to wrap various issues during PDisjunction rewriting. | ||
15 | * @author Zoltan Ujhelyi | ||
16 | * | ||
17 | */ | ||
18 | public class RewriterException extends QueryInitializationException { | ||
19 | |||
20 | private static final long serialVersionUID = -4703825954995497932L; | ||
21 | |||
22 | public RewriterException(String message, String[] context, String shortMessage, Object patternDescription, | ||
23 | Throwable cause) { | ||
24 | super(message, context, shortMessage, patternDescription, cause); | ||
25 | } | ||
26 | |||
27 | public RewriterException(String message, String[] context, String shortMessage, Object patternDescription) { | ||
28 | super(message, context, shortMessage, patternDescription); | ||
29 | } | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java new file mode 100644 index 00000000..71459558 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java | |||
@@ -0,0 +1,63 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, 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.LinkedHashSet; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
15 | import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; | ||
21 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
22 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; | ||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
25 | |||
26 | /** | ||
27 | * @author Zoltan Ujhelyi | ||
28 | * | ||
29 | */ | ||
30 | public class SurrogateQueryRewriter extends PDisjunctionRewriter { | ||
31 | |||
32 | @Override | ||
33 | public PDisjunction rewrite(PDisjunction disjunction) { | ||
34 | Set<PBody> replacedBodies = new LinkedHashSet<>(); | ||
35 | for (PBody body : disjunction.getBodies()) { | ||
36 | PBodyCopier copier = new PBodyCopier(body, getTraceCollector()) { | ||
37 | |||
38 | @Override | ||
39 | protected void copyTypeConstraint(TypeConstraint typeConstraint) { | ||
40 | PVariable[] mappedVariables = extractMappedVariables(typeConstraint); | ||
41 | Tuple variablesTuple = Tuples.flatTupleOf((Object[])mappedVariables); | ||
42 | final IInputKey supplierKey = typeConstraint.getSupplierKey(); | ||
43 | if(SurrogateQueryRegistry.instance().hasSurrogateQueryFQN(supplierKey)) { | ||
44 | PQuery surrogateQuery = SurrogateQueryRegistry.instance().getSurrogateQuery(supplierKey); | ||
45 | if (surrogateQuery == null) { | ||
46 | throw new IllegalStateException( | ||
47 | String.format("Surrogate query for feature %s not found", | ||
48 | supplierKey.getPrettyPrintableName())); | ||
49 | } | ||
50 | addTrace(typeConstraint, new PositivePatternCall(getCopiedBody(), variablesTuple, surrogateQuery)); | ||
51 | } else { | ||
52 | addTrace(typeConstraint, new TypeConstraint(getCopiedBody(), variablesTuple, supplierKey)); | ||
53 | } | ||
54 | } | ||
55 | }; | ||
56 | PBody modifiedBody = copier.getCopiedBody(); | ||
57 | replacedBodies.add(modifiedBody); | ||
58 | modifiedBody.setStatus(PQueryStatus.OK); | ||
59 | } | ||
60 | return new PDisjunction(replacedBodies); | ||
61 | } | ||
62 | |||
63 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java new file mode 100644 index 00000000..10337979 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. | ||
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.HashMap; | ||
12 | import java.util.LinkedHashMap; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
19 | |||
20 | /** | ||
21 | * A wrapper for {@link IExpressionEvaluator} which is capable of correctly mapping variable names used by the | ||
22 | * expression. | ||
23 | * | ||
24 | * @author Grill Balázs | ||
25 | * | ||
26 | */ | ||
27 | class VariableMappingExpressionEvaluatorWrapper implements IExpressionEvaluator { | ||
28 | |||
29 | private final IExpressionEvaluator wrapped; | ||
30 | private final Map<String, String> variableMapping; | ||
31 | |||
32 | public VariableMappingExpressionEvaluatorWrapper(IExpressionEvaluator wrapped, | ||
33 | Map<PVariable, PVariable> variableMapping) { | ||
34 | |||
35 | // Support to rewrap an already wrapped expression. | ||
36 | boolean rewrap = wrapped instanceof VariableMappingExpressionEvaluatorWrapper; | ||
37 | this.wrapped = rewrap ? ((VariableMappingExpressionEvaluatorWrapper)wrapped).wrapped : wrapped; | ||
38 | |||
39 | // Instead of just saving the reference of the mapping, save the actual (trimmed) state of the mapping as it | ||
40 | // may change during copying (especially during flattening). A LinkedHashMap is used to retain ordering of | ||
41 | // original parameter names iterator. | ||
42 | this.variableMapping = new LinkedHashMap<>(); | ||
43 | |||
44 | // Index map by variable names | ||
45 | Map<String, PVariable> names = new HashMap<>(); | ||
46 | for (PVariable originalVar : variableMapping.keySet()) { | ||
47 | names.put(originalVar.getName(), originalVar); | ||
48 | } | ||
49 | |||
50 | // In case of rewrapping, current names are contained by the previous mapping | ||
51 | Map<String, String> previousMapping = null; | ||
52 | if (rewrap){ | ||
53 | previousMapping = ((VariableMappingExpressionEvaluatorWrapper)wrapped).variableMapping; | ||
54 | } | ||
55 | |||
56 | // Populate mapping | ||
57 | for (String inputParameterName : this.wrapped.getInputParameterNames()) { | ||
58 | String parameterName = rewrap ? previousMapping.get(inputParameterName) : inputParameterName; | ||
59 | Preconditions.checkArgument(parameterName != null); | ||
60 | PVariable original = names.get(parameterName); | ||
61 | Preconditions.checkArgument(original != null); | ||
62 | PVariable mapped = variableMapping.get(original); | ||
63 | if (mapped != null){ | ||
64 | this.variableMapping.put(inputParameterName, mapped.getName()); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public String getShortDescription() { | ||
71 | return wrapped.getShortDescription(); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public Iterable<String> getInputParameterNames() { | ||
76 | return variableMapping.values(); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public Object evaluateExpression(final IValueProvider provider) throws Exception { | ||
81 | return wrapped.evaluateExpression(variableName -> { | ||
82 | String mappedVariableName = variableMapping.get(variableName); | ||
83 | Preconditions.checkArgument(mappedVariableName != null, "Could not find variable %s", variableName); | ||
84 | return provider.getValue(mappedVariableName); | ||
85 | }); | ||
86 | } | ||
87 | |||
88 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java new file mode 100644 index 00000000..a26d9193 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java | |||
@@ -0,0 +1,136 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Objects; | ||
17 | import java.util.Set; | ||
18 | |||
19 | /** | ||
20 | * Common implementation methods for immutable and volatile tuples. The class should not be used directly in client | ||
21 | * code, except for the definition of new tuple implementations. | ||
22 | * | ||
23 | * @author Zoltan Ujhelyi | ||
24 | * @since 1.7 | ||
25 | */ | ||
26 | public abstract class AbstractTuple implements ITuple { | ||
27 | |||
28 | /** | ||
29 | * As the tuple is supposed to be immutable, do not modify the returned array. | ||
30 | * | ||
31 | * @return the array containing all elements of this Tuple | ||
32 | */ | ||
33 | @Override | ||
34 | public Object[] getElements() { | ||
35 | Object[] allElements = new Object[getSize()]; | ||
36 | for (int i = 0; i < allElements.length; ++i) | ||
37 | allElements[i] = get(i); | ||
38 | return allElements; | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * @return the set containing all distinct elements of this Tuple, cast as type T | ||
43 | */ | ||
44 | @Override | ||
45 | @SuppressWarnings("unchecked") | ||
46 | public <T> Set<T> getDistinctElements() { | ||
47 | Set<T> result = new HashSet<T>(); | ||
48 | Object[] elements = getElements(); | ||
49 | for (Object object : elements) { | ||
50 | result.add((T) object); | ||
51 | } | ||
52 | return result; | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) | ||
57 | * occurrence is calculated. | ||
58 | * | ||
59 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
60 | */ | ||
61 | @Override | ||
62 | public Map<Object, Integer> invertIndex() { | ||
63 | Map<Object, Integer> result = new HashMap<Object, Integer>(); | ||
64 | for (int i = 0; i < getSize(); i++) | ||
65 | result.put(get(i), i); | ||
66 | return result; | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its | ||
71 | * occurrences is calculated. | ||
72 | * | ||
73 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
74 | */ | ||
75 | @Override | ||
76 | public Map<Object, List<Integer>> invertIndexWithMupliplicity() { | ||
77 | Map<Object, List<Integer>> result = new HashMap<Object, List<Integer>>(); | ||
78 | for (int i = 0; i < getSize(); i++) { | ||
79 | Object value = get(i); | ||
80 | List<Integer> indices = result.computeIfAbsent(value, v -> new ArrayList<>()); | ||
81 | indices.add(i); | ||
82 | } | ||
83 | return result; | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * @since 1.7 | ||
88 | */ | ||
89 | protected IndexOutOfBoundsException raiseIndexingError(int index) { | ||
90 | return new IndexOutOfBoundsException( | ||
91 | String.format("No value at position %d for %s instance %s", index, getClass().getSimpleName(), this)); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Compares the elements stored in this tuple to another tuple | ||
96 | */ | ||
97 | protected boolean internalEquals(ITuple other) { | ||
98 | if (getSize() != other.getSize()) | ||
99 | return false; | ||
100 | boolean result = true; | ||
101 | for (int i = 0; result && i < getSize(); ++i) { | ||
102 | Object ours = get(i); | ||
103 | Object theirs = other.get(i); | ||
104 | result = result && Objects.equals(ours, theirs); | ||
105 | } | ||
106 | return result; | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public String toString() { | ||
111 | StringBuilder s = new StringBuilder(); | ||
112 | s.append("T("); | ||
113 | for (Object o : getElements()) { | ||
114 | s.append(o == null ? "null" : o.toString()); | ||
115 | s.append(';'); | ||
116 | } | ||
117 | s.append(')'); | ||
118 | return s.toString(); | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * @since 1.7 | ||
123 | */ | ||
124 | protected int doCalcHash() { | ||
125 | final int PRIME = 31; | ||
126 | int hash = 1; | ||
127 | for (int i = 0; i < getSize(); i++) { | ||
128 | hash = PRIME * hash; | ||
129 | Object element = get(i); | ||
130 | if (element != null) | ||
131 | hash += element.hashCode(); | ||
132 | } | ||
133 | return hash; | ||
134 | } | ||
135 | |||
136 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java new file mode 100644 index 00000000..6f46b763 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java | |||
@@ -0,0 +1,20 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Base class for all flat tuple implementations. | ||
13 | * Flat tuples store all elements locally (do not reference other tuples). | ||
14 | * | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.7 | ||
17 | */ | ||
18 | public abstract class BaseFlatTuple extends Tuple { | ||
19 | |||
20 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java new file mode 100644 index 00000000..03f9ea89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java | |||
@@ -0,0 +1,65 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Common functionality of left inheritance tuple implementations. | ||
13 | * | ||
14 | * <p>Left inheritance tuples inherit their first few elements from another tuple, | ||
15 | * and extend it with additional "local" elements. | ||
16 | * | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.7 | ||
19 | */ | ||
20 | public abstract class BaseLeftInheritanceTuple extends Tuple { | ||
21 | |||
22 | /** | ||
23 | * The number of elements that aren't stored locally, but inherited from an ancestor Tuple instead. | ||
24 | */ | ||
25 | protected final int inheritedIndex; | ||
26 | /** | ||
27 | * This object contains the same elements as the ancestor on the first inheritedIndex positions | ||
28 | */ | ||
29 | protected final Tuple ancestor; | ||
30 | |||
31 | /** | ||
32 | * @param ancestor | ||
33 | */ | ||
34 | public BaseLeftInheritanceTuple(Tuple ancestor) { | ||
35 | super(); | ||
36 | this.ancestor = ancestor; | ||
37 | this.inheritedIndex = ancestor.getSize(); | ||
38 | } | ||
39 | |||
40 | /** | ||
41 | * @return the number of local (non-inherited) elements | ||
42 | */ | ||
43 | public abstract int getLocalSize(); | ||
44 | |||
45 | /** | ||
46 | * Optimized equals calculation (prediction: true, since hash values match) | ||
47 | */ | ||
48 | @Override | ||
49 | protected boolean internalEquals(ITuple other) { | ||
50 | if (other instanceof BaseLeftInheritanceTuple) { | ||
51 | BaseLeftInheritanceTuple blit = (BaseLeftInheritanceTuple) other; | ||
52 | if (blit.inheritedIndex == this.inheritedIndex) { | ||
53 | if (this.ancestor.equals(blit.ancestor)) { | ||
54 | return localEquals(blit); | ||
55 | } else return false; | ||
56 | } | ||
57 | } | ||
58 | return super.internalEquals(other); | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Checks the equivalence of local elements only, after ancestor tuple has been determined to be equal. | ||
63 | */ | ||
64 | protected abstract boolean localEquals(BaseLeftInheritanceTuple other); | ||
65 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java new file mode 100644 index 00000000..8bbb0ac2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java | |||
@@ -0,0 +1,60 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | |||
14 | /** | ||
15 | * Default Tuple implementation, with statically unknown arity. | ||
16 | * @author Gabor Bergmann | ||
17 | */ | ||
18 | public final class FlatTuple extends BaseFlatTuple { | ||
19 | |||
20 | /** | ||
21 | * Array of substituted values. DO NOT MODIFY! Use Constructor to build a new instance instead. | ||
22 | */ | ||
23 | private final Object[] elements; | ||
24 | |||
25 | /** | ||
26 | * Creates a FlatTuple instance, fills it with the given array. | ||
27 | * <p> Users should consider calling {@link Tuples#flatTupleOf(Object...)} instead to save memory on low-arity tuples. | ||
28 | * | ||
29 | * @param elements | ||
30 | * array of substitution values | ||
31 | */ | ||
32 | protected FlatTuple(Object... elements) { | ||
33 | this.elements = Arrays.copyOf(elements, elements.length); | ||
34 | calcHash(); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Object get(int index) { | ||
39 | return elements[index]; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public int getSize() { | ||
44 | return elements.length; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public Object[] getElements() { | ||
49 | return elements; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | protected boolean internalEquals(ITuple other) { | ||
54 | if (other instanceof FlatTuple) { | ||
55 | return Arrays.equals(elements, ((FlatTuple) other).elements); | ||
56 | } else | ||
57 | return super.internalEquals(other); | ||
58 | } | ||
59 | |||
60 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java new file mode 100644 index 00000000..93f412b7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Flat tuple with statically known arity of 0. | ||
13 | * | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | * | ||
17 | */ | ||
18 | public final class FlatTuple0 extends BaseFlatTuple { | ||
19 | protected static final FlatTuple0 INSTANCE = new FlatTuple0(); | ||
20 | |||
21 | private FlatTuple0() { | ||
22 | calcHash(); | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public int getSize() { | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Object get(int index) { | ||
32 | throw raiseIndexingError(index); | ||
33 | } | ||
34 | |||
35 | private static final Object[] NULLARY_ARRAY = new Object[0]; | ||
36 | |||
37 | @Override | ||
38 | public Object[] getElements() { | ||
39 | return NULLARY_ARRAY; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | protected boolean internalEquals(ITuple other) { | ||
44 | return 0 == other.getSize(); | ||
45 | } | ||
46 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java new file mode 100644 index 00000000..b3b1c312 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 1. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple1 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | |||
23 | protected FlatTuple1(Object element0) { | ||
24 | this.element0 = element0; | ||
25 | calcHash(); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public int getSize() { | ||
30 | return 1; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public Object get(int index) { | ||
35 | if (index == 0) return element0; | ||
36 | else throw raiseIndexingError(index); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | protected boolean internalEquals(ITuple other) { | ||
41 | return 1 == other.getSize() && | ||
42 | Objects.equals(element0, other.get(0)); | ||
43 | } | ||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java new file mode 100644 index 00000000..2dcfd718 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java | |||
@@ -0,0 +1,51 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 2. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple2 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | private final Object element1; | ||
23 | |||
24 | protected FlatTuple2(Object element0, Object element1) { | ||
25 | this.element0 = element0; | ||
26 | this.element1 = element1; | ||
27 | calcHash(); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public int getSize() { | ||
32 | return 2; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Object get(int index) { | ||
37 | switch(index) { | ||
38 | case 0 : return element0; | ||
39 | case 1 : return element1; | ||
40 | default: throw raiseIndexingError(index); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | protected boolean internalEquals(ITuple other) { | ||
46 | return 2 == other.getSize() && | ||
47 | Objects.equals(element0, other.get(0)) && | ||
48 | Objects.equals(element1, other.get(1)); | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java new file mode 100644 index 00000000..50cee57e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 3. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple3 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | private final Object element1; | ||
23 | private final Object element2; | ||
24 | |||
25 | protected FlatTuple3(Object element0, Object element1, Object element2) { | ||
26 | this.element0 = element0; | ||
27 | this.element1 = element1; | ||
28 | this.element2 = element2; | ||
29 | calcHash(); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public int getSize() { | ||
34 | return 3; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public Object get(int index) { | ||
39 | switch (index) { | ||
40 | case 0: return element0; | ||
41 | case 1: return element1; | ||
42 | case 2: return element2; | ||
43 | default: throw raiseIndexingError(index); | ||
44 | } | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | protected boolean internalEquals(ITuple other) { | ||
49 | return 3 == other.getSize() && | ||
50 | Objects.equals(element0, other.get(0)) && | ||
51 | Objects.equals(element1, other.get(1)) && | ||
52 | Objects.equals(element2, other.get(2)); | ||
53 | } | ||
54 | |||
55 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java new file mode 100644 index 00000000..024c94f4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java | |||
@@ -0,0 +1,59 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * Flat tuple with statically known arity of 4. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public final class FlatTuple4 extends BaseFlatTuple { | ||
21 | private final Object element0; | ||
22 | private final Object element1; | ||
23 | private final Object element2; | ||
24 | private final Object element3; | ||
25 | |||
26 | protected FlatTuple4(Object element0, Object element1, Object element2, Object element3) { | ||
27 | this.element0 = element0; | ||
28 | this.element1 = element1; | ||
29 | this.element2 = element2; | ||
30 | this.element3 = element3; | ||
31 | calcHash(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public int getSize() { | ||
36 | return 4; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Object get(int index) { | ||
41 | switch(index) { | ||
42 | case 0: return element0; | ||
43 | case 1: return element1; | ||
44 | case 2: return element2; | ||
45 | case 3: return element3; | ||
46 | default: throw raiseIndexingError(index); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | protected boolean internalEquals(ITuple other) { | ||
52 | return 4 == other.getSize() && | ||
53 | Objects.equals(element0, other.get(0)) && | ||
54 | Objects.equals(element1, other.get(1)) && | ||
55 | Objects.equals(element2, other.get(2)) && | ||
56 | Objects.equals(element3, other.get(3)); | ||
57 | } | ||
58 | |||
59 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java new file mode 100644 index 00000000..f5dab2a5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.tuple; | ||
10 | |||
11 | /** | ||
12 | * A tuple that allows modifying the underlying value. Should not be used for non-volatile tuples. | ||
13 | * | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public interface IModifiableTuple extends ITuple { | ||
18 | |||
19 | /** | ||
20 | * Sets the selected value for a tuple | ||
21 | * | ||
22 | * @pre: 0 <= index < getSize() | ||
23 | * | ||
24 | */ | ||
25 | void set(int index, Object value); | ||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java new file mode 100644 index 00000000..92014781 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java | |||
@@ -0,0 +1,64 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * Represents both mutable and immutable tuples | ||
17 | * | ||
18 | * @author Zoltan Ujhelyi | ||
19 | * @since 1.7 | ||
20 | * | ||
21 | */ | ||
22 | public interface ITuple { | ||
23 | |||
24 | /** | ||
25 | * @pre: 0 <= index < getSize() | ||
26 | * | ||
27 | * @return the element at the specified index | ||
28 | */ | ||
29 | Object get(int index); | ||
30 | |||
31 | /** | ||
32 | * As the tuple is supposed to be immutable, do not modify the returned array. | ||
33 | * @return the array containing all elements of this Tuple | ||
34 | */ | ||
35 | Object[] getElements(); | ||
36 | |||
37 | /** | ||
38 | * @return the set containing all distinct elements of this Tuple, cast as type T | ||
39 | */ | ||
40 | <T> Set<T> getDistinctElements(); | ||
41 | |||
42 | /** | ||
43 | * @return number of elements | ||
44 | */ | ||
45 | int getSize(); | ||
46 | |||
47 | /** | ||
48 | * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) | ||
49 | * occurrence is calculated. | ||
50 | * | ||
51 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
52 | */ | ||
53 | Map<Object, Integer> invertIndex(); | ||
54 | |||
55 | /** | ||
56 | * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its | ||
57 | * occurrences is calculated. | ||
58 | * | ||
59 | * @return the inverted index mapping each element of this pattern to its index in the array | ||
60 | */ | ||
61 | Map<Object, List<Integer>> invertIndexWithMupliplicity(); | ||
62 | |||
63 | Tuple toImmutable(); | ||
64 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java new file mode 100644 index 00000000..dcdfc376 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java | |||
@@ -0,0 +1,172 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.Objects; | ||
14 | |||
15 | /** | ||
16 | * | ||
17 | * Tuple that inherits another tuple on the left. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public final class LeftInheritanceTuple extends BaseLeftInheritanceTuple { | ||
23 | /** | ||
24 | * Array of substituted values above inheritedIndex. DO NOT MODIFY! Use Constructor to build a new instance instead. | ||
25 | */ | ||
26 | private final Object[] localElements; | ||
27 | |||
28 | // | ||
29 | // /** | ||
30 | // * Creates a Tuple instance, fills it with the given array. | ||
31 | // * @pre: no elements are null | ||
32 | // * @param elements array of substitution values | ||
33 | // */ | ||
34 | // public Tuple(Object[] elements) | ||
35 | // { | ||
36 | // this.localElements = elements; | ||
37 | // this.ancestor=null; | ||
38 | // this.inheritedIndex = 0; | ||
39 | // calcHash(); | ||
40 | // } | ||
41 | |||
42 | |||
43 | |||
44 | /** | ||
45 | * Creates a Tuple instance, lets it inherit from an ancestor, extends it with a given array. @pre: no elements are | ||
46 | * null | ||
47 | * | ||
48 | * @param localElements | ||
49 | * array of substitution values | ||
50 | */ | ||
51 | LeftInheritanceTuple(Tuple ancestor, Object[] localElements) { | ||
52 | super(ancestor); | ||
53 | this.localElements = localElements; | ||
54 | calcHash(); | ||
55 | } | ||
56 | |||
57 | // | ||
58 | // /** | ||
59 | // * Creates a Tuple instance of size one, fills it with the given object. | ||
60 | // * @pre: o!=null | ||
61 | // * @param o the single substitution | ||
62 | // */ | ||
63 | // public Tuple(Object o) | ||
64 | // { | ||
65 | // localElements = new Object [1]; | ||
66 | // localElements[0] = o; | ||
67 | // this.ancestor=null; | ||
68 | // this.inheritedIndex = 0; | ||
69 | // calcHash(); | ||
70 | // } | ||
71 | // | ||
72 | // /** | ||
73 | // * Creates a Tuple instance of size two, fills it with the given objects. | ||
74 | // * @pre: o1!=null, o2!=null | ||
75 | // */ | ||
76 | // public Tuple(Object o1, Object o2) | ||
77 | // { | ||
78 | // localElements = new Object [2]; | ||
79 | // localElements[0] = o1; | ||
80 | // localElements[1] = o2; | ||
81 | // this.ancestor=null; | ||
82 | // this.inheritedIndex = 0; | ||
83 | // calcHash(); | ||
84 | // } | ||
85 | // | ||
86 | // /** | ||
87 | // * Creates a Tuple instance of size three, fills it with the given | ||
88 | // objects. | ||
89 | // * @pre: o1!=null, o2!=null, o3!=null | ||
90 | // */ | ||
91 | // public Tuple(Object o1, Object o2, Object o3) | ||
92 | // { | ||
93 | // localElements = new Object [3]; | ||
94 | // localElements[0] = o1; | ||
95 | // localElements[1] = o2; | ||
96 | // localElements[2] = o3; | ||
97 | // this.ancestor=null; | ||
98 | // this.inheritedIndex = 0; | ||
99 | // calcHash(); | ||
100 | // } | ||
101 | |||
102 | /** | ||
103 | * @return number of elements | ||
104 | */ | ||
105 | public int getSize() { | ||
106 | return inheritedIndex + localElements.length; | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public int getLocalSize() { | ||
111 | return localElements.length; | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * @pre: 0 <= index < getSize() | ||
116 | * | ||
117 | * @return the element at the specified index | ||
118 | */ | ||
119 | public Object get(int index) { | ||
120 | return (index < inheritedIndex) ? ancestor.get(index) : localElements[index - inheritedIndex]; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Optimized hash calculation | ||
125 | */ | ||
126 | @Override | ||
127 | void calcHash() { | ||
128 | final int PRIME = 31; | ||
129 | cachedHash = ancestor.hashCode(); | ||
130 | for (int i = 0; i < localElements.length; i++) { | ||
131 | cachedHash = PRIME * cachedHash; | ||
132 | Object element = localElements[i]; | ||
133 | if (element != null) | ||
134 | cachedHash += element.hashCode(); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Optimized equals calculation (prediction: true, since hash values match) | ||
140 | */ | ||
141 | @Override | ||
142 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
143 | if (other instanceof LeftInheritanceTuple) { | ||
144 | LeftInheritanceTuple lit = (LeftInheritanceTuple)other; | ||
145 | return Arrays.equals(this.localElements, lit.localElements); | ||
146 | } else { | ||
147 | if (localElements.length != other.getLocalSize()) | ||
148 | return false; | ||
149 | int index = inheritedIndex; | ||
150 | for (int i = 0; i<localElements.length; ++i) { | ||
151 | if (! Objects.equals(localElements[i], other.get(index++))) | ||
152 | return false; | ||
153 | } | ||
154 | return true; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | // public int compareTo(Object arg0) { | ||
159 | // Tuple other = (Tuple) arg0; | ||
160 | // | ||
161 | // int retVal = cachedHash - other.cachedHash; | ||
162 | // if (retVal==0) retVal = elements.length - other.elements.length; | ||
163 | // for (int i=0; retVal==0 && i<elements.length; ++i) | ||
164 | // { | ||
165 | // if (elements[i] == null && other.elements[i] != null) retVal = -1; | ||
166 | // else if (other.elements[i] == null) retVal = 1; | ||
167 | // else retVal = elements[i].compareTo(other.elements[i]); | ||
168 | // } | ||
169 | // return retVal; | ||
170 | // } | ||
171 | |||
172 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java new file mode 100644 index 00000000..61123176 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java | |||
@@ -0,0 +1,83 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple1 extends BaseLeftInheritanceTuple { | ||
18 | /** | ||
19 | * A single substituted value after inheritedIndex. | ||
20 | */ | ||
21 | private final Object localElement; | ||
22 | |||
23 | /** | ||
24 | * @param ancestor | ||
25 | * @param localElement | ||
26 | */ | ||
27 | protected LeftInheritanceTuple1(Tuple ancestor, Object localElement) { | ||
28 | super(ancestor); | ||
29 | this.localElement = localElement; | ||
30 | calcHash(); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * @return number of elements | ||
35 | */ | ||
36 | public int getSize() { | ||
37 | return inheritedIndex + 1; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public int getLocalSize() { | ||
42 | return 1; | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * @pre: 0 <= index < getSize() | ||
47 | * | ||
48 | * @return the element at the specified index | ||
49 | */ | ||
50 | public Object get(int index) { | ||
51 | int local = index - inheritedIndex; | ||
52 | if (local < 0) | ||
53 | return ancestor.get(index); | ||
54 | else if (local == 0) return localElement; | ||
55 | else throw raiseIndexingError(index); | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * Optimized hash calculation | ||
60 | */ | ||
61 | @Override | ||
62 | void calcHash() { | ||
63 | final int PRIME = 31; | ||
64 | cachedHash = ancestor.hashCode(); | ||
65 | cachedHash = PRIME * cachedHash; | ||
66 | if (localElement != null) cachedHash += localElement.hashCode(); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * Optimized equals calculation (prediction: true, since hash values match) | ||
71 | */ | ||
72 | @Override | ||
73 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
74 | if (other instanceof LeftInheritanceTuple1) { | ||
75 | LeftInheritanceTuple1 lit = (LeftInheritanceTuple1)other; | ||
76 | return Objects.equals(this.localElement, lit.localElement); | ||
77 | } else { | ||
78 | return (1 == other.getLocalSize()) && | ||
79 | Objects.equals(localElement, other.get(inheritedIndex)); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java new file mode 100644 index 00000000..e9fb98e8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java | |||
@@ -0,0 +1,73 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple2 extends BaseLeftInheritanceTuple { | ||
18 | private final Object localElement0; | ||
19 | private final Object localElement1; | ||
20 | |||
21 | protected LeftInheritanceTuple2(Tuple ancestor, Object localElement0, Object localElement1) { | ||
22 | super(ancestor); | ||
23 | this.localElement0 = localElement0; | ||
24 | this.localElement1 = localElement1; | ||
25 | calcHash(); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public int getLocalSize() { | ||
30 | return 2; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public int getSize() { | ||
35 | return inheritedIndex + 2; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Object get(int index) { | ||
40 | int local = index - inheritedIndex; | ||
41 | if (local < 0) | ||
42 | return ancestor.get(index); | ||
43 | else if (local == 0) return localElement0; | ||
44 | else if (local == 1) return localElement1; | ||
45 | else throw raiseIndexingError(index); | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Optimized hash calculation | ||
50 | */ | ||
51 | @Override | ||
52 | void calcHash() { | ||
53 | final int PRIME = 31; | ||
54 | cachedHash = ancestor.hashCode(); | ||
55 | cachedHash = PRIME * cachedHash; | ||
56 | if (localElement0 != null) cachedHash += localElement0.hashCode(); | ||
57 | cachedHash = PRIME * cachedHash; | ||
58 | if (localElement1 != null) cachedHash += localElement1.hashCode(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
63 | if (other instanceof LeftInheritanceTuple2) { | ||
64 | LeftInheritanceTuple2 lit = (LeftInheritanceTuple2)other; | ||
65 | return Objects.equals(this.localElement0, lit.localElement0) && | ||
66 | Objects.equals(this.localElement1, lit.localElement1); | ||
67 | } else { | ||
68 | return (2 == other.getLocalSize()) && | ||
69 | Objects.equals(localElement0, other.get(inheritedIndex)) && | ||
70 | Objects.equals(localElement1, other.get(inheritedIndex + 1)); | ||
71 | } | ||
72 | } | ||
73 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java new file mode 100644 index 00000000..e61d196f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java | |||
@@ -0,0 +1,81 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple3 extends BaseLeftInheritanceTuple { | ||
18 | private final Object localElement0; | ||
19 | private final Object localElement1; | ||
20 | private final Object localElement2; | ||
21 | |||
22 | protected LeftInheritanceTuple3(Tuple ancestor, Object localElement0, Object localElement1, Object localElement2) { | ||
23 | super(ancestor); | ||
24 | this.localElement0 = localElement0; | ||
25 | this.localElement1 = localElement1; | ||
26 | this.localElement2 = localElement2; | ||
27 | calcHash(); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public int getLocalSize() { | ||
32 | return 3; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public int getSize() { | ||
37 | return inheritedIndex + 3; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Object get(int index) { | ||
42 | int local = index - inheritedIndex; | ||
43 | if (local < 0) | ||
44 | return ancestor.get(index); | ||
45 | else if (local == 0) return localElement0; | ||
46 | else if (local == 1) return localElement1; | ||
47 | else if (local == 2) return localElement2; | ||
48 | else throw raiseIndexingError(index); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Optimized hash calculation | ||
53 | */ | ||
54 | @Override | ||
55 | void calcHash() { | ||
56 | final int PRIME = 31; | ||
57 | cachedHash = ancestor.hashCode(); | ||
58 | cachedHash = PRIME * cachedHash; | ||
59 | if (localElement0 != null) cachedHash += localElement0.hashCode(); | ||
60 | cachedHash = PRIME * cachedHash; | ||
61 | if (localElement1 != null) cachedHash += localElement1.hashCode(); | ||
62 | cachedHash = PRIME * cachedHash; | ||
63 | if (localElement2 != null) cachedHash += localElement2.hashCode(); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
68 | if (other instanceof LeftInheritanceTuple3) { | ||
69 | LeftInheritanceTuple3 lit = (LeftInheritanceTuple3)other; | ||
70 | return Objects.equals(this.localElement0, lit.localElement0) && | ||
71 | Objects.equals(this.localElement1, lit.localElement1) && | ||
72 | Objects.equals(this.localElement2, lit.localElement2); | ||
73 | } else { | ||
74 | return (3 == other.getLocalSize()) && | ||
75 | Objects.equals(localElement0, other.get(inheritedIndex)) && | ||
76 | Objects.equals(localElement1, other.get(inheritedIndex + 1)) && | ||
77 | Objects.equals(localElement2, other.get(inheritedIndex + 2)); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java new file mode 100644 index 00000000..4e50f1e1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class LeftInheritanceTuple4 extends BaseLeftInheritanceTuple { | ||
18 | private final Object localElement0; | ||
19 | private final Object localElement1; | ||
20 | private final Object localElement2; | ||
21 | private final Object localElement3; | ||
22 | |||
23 | protected LeftInheritanceTuple4(Tuple ancestor, Object localElement0, Object localElement1, Object localElement2, | ||
24 | Object localElement3) { | ||
25 | super(ancestor); | ||
26 | this.localElement0 = localElement0; | ||
27 | this.localElement1 = localElement1; | ||
28 | this.localElement2 = localElement2; | ||
29 | this.localElement3 = localElement3; | ||
30 | calcHash(); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public int getLocalSize() { | ||
35 | return 4; | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public int getSize() { | ||
40 | return inheritedIndex + 4; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public Object get(int index) { | ||
45 | int local = index - inheritedIndex; | ||
46 | if (local < 0) | ||
47 | return ancestor.get(index); | ||
48 | else if (local == 0) return localElement0; | ||
49 | else if (local == 1) return localElement1; | ||
50 | else if (local == 2) return localElement2; | ||
51 | else if (local == 3) return localElement3; | ||
52 | else throw raiseIndexingError(index); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Optimized hash calculation | ||
57 | */ | ||
58 | @Override | ||
59 | void calcHash() { | ||
60 | final int PRIME = 31; | ||
61 | cachedHash = ancestor.hashCode(); | ||
62 | cachedHash = PRIME * cachedHash; | ||
63 | if (localElement0 != null) cachedHash += localElement0.hashCode(); | ||
64 | cachedHash = PRIME * cachedHash; | ||
65 | if (localElement1 != null) cachedHash += localElement1.hashCode(); | ||
66 | cachedHash = PRIME * cachedHash; | ||
67 | if (localElement2 != null) cachedHash += localElement2.hashCode(); | ||
68 | cachedHash = PRIME * cachedHash; | ||
69 | if (localElement3 != null) cachedHash += localElement3.hashCode(); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | protected boolean localEquals(BaseLeftInheritanceTuple other) { | ||
74 | if (other instanceof LeftInheritanceTuple4) { | ||
75 | LeftInheritanceTuple4 lit = (LeftInheritanceTuple4)other; | ||
76 | return Objects.equals(this.localElement0, lit.localElement0) && | ||
77 | Objects.equals(this.localElement1, lit.localElement1) && | ||
78 | Objects.equals(this.localElement2, lit.localElement2) && | ||
79 | Objects.equals(this.localElement3, lit.localElement3); | ||
80 | } else { | ||
81 | return (4 == other.getLocalSize()) && | ||
82 | Objects.equals(localElement0, other.get(inheritedIndex)) && | ||
83 | Objects.equals(localElement1, other.get(inheritedIndex + 1)) && | ||
84 | Objects.equals(localElement2, other.get(inheritedIndex + 2)) && | ||
85 | Objects.equals(localElement3, other.get(inheritedIndex + 3)); | ||
86 | } | ||
87 | } | ||
88 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java new file mode 100644 index 00000000..a5d1991c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2009 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | /** | ||
13 | * A tuple that transparently provides a masked (transformed) view of another tuple. | ||
14 | * | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 2.0 | ||
17 | * | ||
18 | */ | ||
19 | public class MaskedTuple extends Tuple { | ||
20 | |||
21 | Tuple wrapped; | ||
22 | TupleMask mask; | ||
23 | |||
24 | public MaskedTuple(Tuple wrapped, TupleMask mask) { | ||
25 | super(); | ||
26 | // if (wrapped instanceof MaskedTuple) { | ||
27 | // MaskedTuple parent = (MaskedTuple)wrapped; | ||
28 | // this.wrapped = parent.wrapped; | ||
29 | // this.mask = mask.transform(parent.mask); | ||
30 | // } | ||
31 | // else | ||
32 | //{ | ||
33 | this.wrapped = wrapped; | ||
34 | this.mask = mask; | ||
35 | //} | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Object get(int index) { | ||
40 | return wrapped.get(mask.indices[index]); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public int getSize() { | ||
45 | return mask.indices.length; | ||
46 | } | ||
47 | |||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java new file mode 100644 index 00000000..d94f545f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java | |||
@@ -0,0 +1,69 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | /** | ||
13 | * Immutable tuple. Obtain instances via utility class {@link Tuples}. | ||
14 | * @author Gabor Bergmann | ||
15 | * | ||
16 | */ | ||
17 | public abstract class Tuple extends AbstractTuple { | ||
18 | |||
19 | /** | ||
20 | * Caches precalculated hash value | ||
21 | */ | ||
22 | protected int cachedHash; | ||
23 | |||
24 | /** | ||
25 | * Creates a Tuple instance Derivatives should call calcHash() | ||
26 | */ | ||
27 | protected Tuple() { | ||
28 | // calcHash(); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Hash calculation. Overrides should keep semantics. | ||
33 | */ | ||
34 | void calcHash() { | ||
35 | cachedHash = doCalcHash(); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public boolean equals(Object obj) { | ||
40 | if (this == obj) | ||
41 | return true; | ||
42 | if (obj instanceof ITuple) { | ||
43 | final ITuple other = (ITuple) obj; | ||
44 | return cachedHash == other.hashCode() && internalEquals(other); | ||
45 | } | ||
46 | return false; | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public int hashCode() { | ||
51 | // Calculated by #calcHash | ||
52 | return cachedHash; | ||
53 | } | ||
54 | |||
55 | public Tuple replaceAll(Object obsolete, Object replacement) { | ||
56 | Object[] oldElements = getElements(); | ||
57 | Object[] newElements = new Object[oldElements.length]; | ||
58 | for (int i = 0; i < oldElements.length; ++i) { | ||
59 | newElements[i] = obsolete.equals(oldElements[i]) ? replacement : oldElements[i]; | ||
60 | } | ||
61 | return Tuples.flatTupleOf(newElements); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public Tuple toImmutable() { | ||
66 | return this; | ||
67 | } | ||
68 | |||
69 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java new file mode 100644 index 00000000..49c55fef --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java | |||
@@ -0,0 +1,560 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Arrays; | ||
14 | import java.util.Collection; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.LinkedList; | ||
17 | import java.util.List; | ||
18 | import java.util.OptionalInt; | ||
19 | import java.util.Set; | ||
20 | |||
21 | /** | ||
22 | * | ||
23 | * Specifies select indices of a tuple. If viewed through this mask (see {@link #transform(ITuple)}), the signature of the pattern will consist of its | ||
24 | * individual substitutions at the given positions, in the exact same order as they appear in indices[]. | ||
25 | * | ||
26 | * @author Gabor Bergmann | ||
27 | */ | ||
28 | public class TupleMask { | ||
29 | /** | ||
30 | * indices[i] specifies the index of the substitution in the original tuple that occupies the i-th place in the | ||
31 | * masked signature. | ||
32 | */ | ||
33 | public final int[] indices; | ||
34 | /** | ||
35 | * the size of the tuple this mask is applied to | ||
36 | */ | ||
37 | public int sourceWidth; | ||
38 | /** | ||
39 | * indicesSorted is indices, sorted in ascending order. | ||
40 | * null by default, call {@link #ensureIndicesSorted()} before using | ||
41 | */ | ||
42 | int[] indicesSorted; | ||
43 | |||
44 | /** | ||
45 | * true if no index occurs twice; computed on demand by {@link #isNonrepeating()} | ||
46 | */ | ||
47 | Boolean isNonrepeating; | ||
48 | |||
49 | /** | ||
50 | * Creates a TupleMask instance with the given indices array | ||
51 | * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known. | ||
52 | * @since 2.0 | ||
53 | */ | ||
54 | protected TupleMask(int[] indices, int sourceWidth, int[] indicesSorted, Boolean isNonrepeating) { | ||
55 | this.indices = indices; | ||
56 | this.sourceWidth = sourceWidth; | ||
57 | this.indicesSorted = indicesSorted; | ||
58 | this.isNonrepeating = isNonrepeating; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Creates a TupleMask instance that selects given positions. | ||
63 | * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. | ||
64 | * | ||
65 | * <p> indicesSorted and isNonrepeating may be OPTIONALLY given if known. | ||
66 | * @since 2.0 | ||
67 | */ | ||
68 | protected static TupleMask fromSelectedIndicesInternal( | ||
69 | int[] selectedIndices, int sourceArity, | ||
70 | int[] indicesSorted, Boolean isNonrepeating) | ||
71 | { | ||
72 | if (selectedIndices.length == 0) // is it nullary? | ||
73 | return new TupleMask0(sourceArity); | ||
74 | |||
75 | // is it identity? | ||
76 | boolean identity = sourceArity == selectedIndices.length; | ||
77 | if (identity) { | ||
78 | for (int k=0; k < sourceArity; ++k) { | ||
79 | if (selectedIndices[k] != k) { | ||
80 | identity = false; | ||
81 | break; | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | if (identity) | ||
86 | return new TupleMaskIdentity(selectedIndices, sourceArity); | ||
87 | |||
88 | // generic case | ||
89 | return new TupleMask(selectedIndices, sourceArity, indicesSorted, isNonrepeating); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Creates a TupleMask instance that selects given positions in monotonically increasing order. | ||
94 | * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. | ||
95 | * @since 2.0 | ||
96 | */ | ||
97 | protected static TupleMask fromSelectedMonotonicIndicesInternal(int[] selectedIndices, int sourceArity) | ||
98 | { | ||
99 | return fromSelectedIndicesInternal(selectedIndices, sourceArity, selectedIndices /* also sorted */, true); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Creates a TupleMask instance of the given size that maps the first 'size' elements intact | ||
104 | */ | ||
105 | public static TupleMask linear(int size, int sourceWidth) { | ||
106 | if (size == sourceWidth) return new TupleMaskIdentity(sourceWidth); | ||
107 | int[] indices = constructLinearSequence(size); | ||
108 | return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * An array containing the first {@link size} nonnegative integers in order | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | protected static int[] constructLinearSequence(int size) { | ||
116 | int[] indices = new int[size]; | ||
117 | for (int i = 0; i < size; i++) | ||
118 | indices[i] = i; | ||
119 | return indices; | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * Creates a TupleMask instance of the given size that maps every single element intact | ||
124 | */ | ||
125 | public static TupleMask identity(int size) { | ||
126 | return new TupleMaskIdentity(size); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * Creates a TupleMask instance of the given size that does not emit output. | ||
131 | */ | ||
132 | public static TupleMask empty(int sourceWidth) { | ||
133 | return linear(0, sourceWidth); | ||
134 | } | ||
135 | |||
136 | /** | ||
137 | * Creates a TupleMask instance that maps the tuple intact save for a single element at the specified index which is | ||
138 | * omitted | ||
139 | */ | ||
140 | public static TupleMask omit(int omission, int sourceWidth) { | ||
141 | int size = sourceWidth - 1; | ||
142 | int[] indices = new int[size]; | ||
143 | for (int i = 0; i < omission; i++) | ||
144 | indices[i] = i; | ||
145 | for (int i = omission; i < size; i++) | ||
146 | indices[i] = i + 1; | ||
147 | return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); | ||
148 | } | ||
149 | |||
150 | |||
151 | /** | ||
152 | * Creates a TupleMask instance that selects positions where keep is true | ||
153 | * @since 1.7 | ||
154 | */ | ||
155 | public static TupleMask fromKeepIndicators(boolean[] keep) { | ||
156 | int size = 0; | ||
157 | for (int k = 0; k < keep.length; ++k) | ||
158 | if (keep[k]) | ||
159 | size++; | ||
160 | if (size == keep.length) return new TupleMaskIdentity(size); | ||
161 | int[] indices = new int[size]; | ||
162 | int l = 0; | ||
163 | for (int k = 0; k < keep.length; ++k) | ||
164 | if (keep[k]) | ||
165 | indices[l++] = k; | ||
166 | return fromSelectedMonotonicIndicesInternal(indices, keep.length); | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * Creates a TupleMask instance that selects given positions. | ||
171 | * @since 1.7 | ||
172 | */ | ||
173 | public static TupleMask fromSelectedIndices(int sourceArity, Collection<Integer> selectedIndices) { | ||
174 | int[] selected = integersToIntArray(selectedIndices); | ||
175 | return fromSelectedIndicesInternal(selected, sourceArity, null, null); | ||
176 | } | ||
177 | /** | ||
178 | * Creates a TupleMask instance that selects given positions. | ||
179 | * @since 1.7 | ||
180 | */ | ||
181 | public static TupleMask fromSelectedIndices(int sourceArity, int[] selectedIndices) { | ||
182 | return fromSelectedIndicesInternal(Arrays.copyOf(selectedIndices, selectedIndices.length), sourceArity, null, null); | ||
183 | } | ||
184 | /** | ||
185 | * Creates a TupleMask instance that selects non-null positions of a given tuple | ||
186 | * @since 1.7 | ||
187 | */ | ||
188 | public static TupleMask fromNonNullIndices(ITuple tuple) { | ||
189 | List<Integer> indices = new ArrayList<>(); | ||
190 | for (int i=0; i < tuple.getSize(); i++) { | ||
191 | if (tuple.get(i) != null) { | ||
192 | indices.add(i); | ||
193 | } | ||
194 | } | ||
195 | if (indices.size() == tuple.getSize()) return new TupleMaskIdentity(indices.size()); | ||
196 | return fromSelectedMonotonicIndicesInternal(integersToIntArray(indices), tuple.getSize()); | ||
197 | } | ||
198 | /** | ||
199 | * @since 1.7 | ||
200 | */ | ||
201 | public static int[] integersToIntArray(Collection<Integer> selectedIndices) { | ||
202 | int[] selected = new int[selectedIndices.size()]; | ||
203 | int k=0; | ||
204 | for (Integer integer : selectedIndices) { | ||
205 | selected[k++] = integer; | ||
206 | } | ||
207 | return selected; | ||
208 | } | ||
209 | |||
210 | |||
211 | /** | ||
212 | * Creates a TupleMask instance that moves an element from one index to other, shifting the others if neccessary. | ||
213 | */ | ||
214 | public static TupleMask displace(int from, int to, int sourceWidth) { | ||
215 | if (from == to) return new TupleMaskIdentity(sourceWidth); | ||
216 | int[] indices = new int[sourceWidth]; | ||
217 | for (int i = 0; i < sourceWidth; i++) | ||
218 | if (i == to) | ||
219 | indices[i] = from; | ||
220 | else if (i >= from && i < to) | ||
221 | indices[i] = i + 1; | ||
222 | else if (i > to && i <= from) | ||
223 | indices[i] = i - 1; | ||
224 | else | ||
225 | indices[i] = i; | ||
226 | return fromSelectedIndicesInternal(indices, sourceWidth, null, true); | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * Creates a TupleMask instance that selects a single element of the tuple. | ||
231 | */ | ||
232 | public static TupleMask selectSingle(int selected, int sourceWidth) { | ||
233 | int[] indices = { selected }; | ||
234 | return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * Creates a TupleMask instance that selects whatever is selected by left, and appends whatever is selected by | ||
239 | * right. PRE: left and right have the same sourcewidth | ||
240 | */ | ||
241 | public static TupleMask append(TupleMask left, TupleMask right) { | ||
242 | int leftLength = left.indices.length; | ||
243 | int rightLength = right.indices.length; | ||
244 | int[] indices = new int[leftLength + rightLength]; | ||
245 | for (int i = 0; i < leftLength; ++i) | ||
246 | indices[i] = left.indices[i]; | ||
247 | for (int i = 0; i < rightLength; ++i) | ||
248 | indices[i + leftLength] = right.indices[i]; | ||
249 | return fromSelectedIndicesInternal(indices, left.sourceWidth, null, null); | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * Generates indicesSorted from indices on demand | ||
254 | */ | ||
255 | void ensureIndicesSorted() { | ||
256 | if (indicesSorted == null) { | ||
257 | indicesSorted = new int[indices.length]; | ||
258 | List<Integer> list = new LinkedList<Integer>(); | ||
259 | for (int i = 0; i < indices.length; ++i) | ||
260 | list.add(indices[i]); | ||
261 | java.util.Collections.sort(list); | ||
262 | int i = 0; | ||
263 | for (Integer a : list) | ||
264 | indicesSorted[i++] = a; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | |||
269 | |||
270 | /** | ||
271 | * Returns the first index of the source that is not selected by the mask, or empty if all indices are selected. | ||
272 | * <p> PRE: mask indices are all different | ||
273 | * @since 2.0 | ||
274 | */ | ||
275 | public OptionalInt getFirstOmittedIndex() { | ||
276 | ensureIndicesSorted(); | ||
277 | int column = 0; | ||
278 | while (column < getSize() && indicesSorted[column] == column) column++; | ||
279 | if (column < getSourceWidth()) return OptionalInt.of(column); | ||
280 | else return OptionalInt.empty(); | ||
281 | } | ||
282 | |||
283 | |||
284 | /** | ||
285 | * Returns a selected masked value from the selected tuple. | ||
286 | * @pre: 0 <= index < getSize() | ||
287 | * @since 1.7 | ||
288 | */ | ||
289 | public Object getValue(ITuple original, int index) { | ||
290 | return original.get(indices[index]); | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * Sets the selected value in the original tuple based on the mask definition | ||
295 | * | ||
296 | * @pre: 0 <= index < getSize() | ||
297 | * @since 1.7 | ||
298 | */ | ||
299 | public void set(IModifiableTuple tuple, int index, Object value) { | ||
300 | tuple.set(indices[index], value); | ||
301 | } | ||
302 | |||
303 | /** | ||
304 | * Generates an immutable, masked view of the original tuple. | ||
305 | * <p> The new tuple will have arity {@link #getSize()}, | ||
306 | * and will consist of the elements of the original tuple, at positions indicated by this mask. | ||
307 | * @since 1.7 | ||
308 | */ | ||
309 | public Tuple transform(ITuple original) { | ||
310 | switch (indices.length) { | ||
311 | case 0: | ||
312 | return FlatTuple0.INSTANCE; | ||
313 | case 1: | ||
314 | return new FlatTuple1(original.get(indices[0])); | ||
315 | case 2: | ||
316 | return new FlatTuple2(original.get(indices[0]), original.get(indices[1])); | ||
317 | case 3: | ||
318 | return new FlatTuple3(original.get(indices[0]), original.get(indices[1]), original.get(indices[2])); | ||
319 | case 4: | ||
320 | return new FlatTuple4(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]), original.get(indices[3])); | ||
321 | default: | ||
322 | Object signature[] = new Object[indices.length]; | ||
323 | for (int i = 0; i < indices.length; ++i) | ||
324 | signature[i] = original.get(indices[i]); | ||
325 | return new FlatTuple(signature); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | /** | ||
330 | * @return true iff no two selected indices are the same | ||
331 | * @since 2.0 | ||
332 | */ | ||
333 | public boolean isNonrepeating() { | ||
334 | if (isNonrepeating == null) { | ||
335 | ensureIndicesSorted(); | ||
336 | int previous = -1; | ||
337 | int i; | ||
338 | for (i = 0; i < sourceWidth && previous != indicesSorted[i]; ++i) { | ||
339 | previous = indicesSorted[i]; | ||
340 | } | ||
341 | isNonrepeating = (i == sourceWidth); // if not, stopped due to detected repetition | ||
342 | } | ||
343 | return isNonrepeating; | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * Returns a tuple `result` that satisfies `this.transform(result).equals(masked)`. Positions of the result tuple | ||
348 | * that are not determined this way will be filled with null. | ||
349 | * | ||
350 | * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true | ||
351 | * @since 1.7 | ||
352 | */ | ||
353 | public Tuple revertFrom(ITuple masked) { | ||
354 | Object[] signature = new Object[sourceWidth]; | ||
355 | for (int i = 0; i < indices.length; ++i) | ||
356 | signature[indices[i]] = masked.get(i); | ||
357 | return Tuples.flatTupleOf(signature); | ||
358 | } | ||
359 | |||
360 | /** | ||
361 | * Returns a tuple `result`, same arity as the original tuple, that satisfies | ||
362 | * `this.transform(result).equals(this.transform(tuple))`. | ||
363 | * Positions of the result tuple that are not determined this way will be filled with null. | ||
364 | * <p> In other words, a copy of the original tuple is returned, | ||
365 | * with null substituted at each position that is <em>not</em> selected by this mask. | ||
366 | * | ||
367 | * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true | ||
368 | * @since 2.1 | ||
369 | */ | ||
370 | public Tuple keepSelectedIndices(ITuple original) { | ||
371 | Object[] signature = new Object[sourceWidth]; | ||
372 | for (int i = 0; i < indices.length; ++i) | ||
373 | signature[indices[i]] = original.get(indices[i]); | ||
374 | return Tuples.flatTupleOf(signature); | ||
375 | } | ||
376 | |||
377 | /** | ||
378 | * Generates an immutable, masked view of the original tuple. | ||
379 | * <p> The list will have arity {@link #getSize()}, | ||
380 | * and will consist of the elements of the original tuple, at positions indicated by this mask. | ||
381 | */ | ||
382 | public <T> List<T> transform(List<T> original) { | ||
383 | List<T> signature = new ArrayList<T>(indices.length); | ||
384 | for (int i = 0; i < indices.length; ++i) | ||
385 | signature.add(original.get(indices[i])); | ||
386 | return signature; | ||
387 | } | ||
388 | |||
389 | /** | ||
390 | * Transforms a given mask directly, instead of transforming tuples that were transformed by the other mask. | ||
391 | * | ||
392 | * @return a mask that cascades the effects this mask after the mask provided as parameter. | ||
393 | */ | ||
394 | public TupleMask transform(TupleMask mask) { | ||
395 | int[] cascadeIndices = new int[indices.length]; | ||
396 | for (int i = 0; i < indices.length; ++i) | ||
397 | cascadeIndices[i] = mask.indices[indices[i]]; | ||
398 | return fromSelectedIndicesInternal(cascadeIndices, mask.sourceWidth, null, null); | ||
399 | } | ||
400 | |||
401 | // /** | ||
402 | // * Generates a complementer mask that maps those elements that were | ||
403 | // untouched by the original mask. | ||
404 | // * Ordering is left intact. | ||
405 | // * A Tuple is used for reference concerning possible equalities among | ||
406 | // elements. | ||
407 | // */ | ||
408 | // public TupleMask complementer(Tuple reference) | ||
409 | // { | ||
410 | // HashSet<Object> touched = new HashSet<Object>(); | ||
411 | // LinkedList<Integer> untouched = new LinkedList<Integer>(); | ||
412 | // | ||
413 | // for (int index : indices) touched.add(reference.get(index)); | ||
414 | // for (int index=0; index<reference.getSize(); ++index) | ||
415 | // { | ||
416 | // if (touched.add(reference.get(index))) untouched.addLast(index); | ||
417 | // } | ||
418 | // | ||
419 | // int[] complementer = new int[untouched.size()]; | ||
420 | // int k = 0; | ||
421 | // for (Integer integer : untouched) complementer[k++] = integer; | ||
422 | // return new TupleMask(complementer, reference.getSize()); | ||
423 | // } | ||
424 | |||
425 | /** | ||
426 | * Combines two substitutions. The new pattern will contain all substitutions of masked and unmasked, assuming that | ||
427 | * the elements of masked indicated by this mask are already matched against unmasked. | ||
428 | * | ||
429 | * POST: the result will start with an exact copy of unmasked | ||
430 | * | ||
431 | * @param unmasked | ||
432 | * primary pattern substitution that is left intact. | ||
433 | * @param masked | ||
434 | * secondary pattern substitution that is transformed to the end of the result. | ||
435 | * @param useInheritance | ||
436 | * whether to use inheritance or copy umasked into result instead. | ||
437 | * @param asComplementer | ||
438 | * whether this mask maps from the masked Tuple to the tail of the result or to the unmasked one. | ||
439 | * @return new pattern that is a combination of unmasked and masked. | ||
440 | */ | ||
441 | public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) { | ||
442 | |||
443 | int combinedLength = asComplementer ? indices.length : masked.getSize() - indices.length; | ||
444 | if (!useInheritance) | ||
445 | combinedLength += unmasked.getSize(); | ||
446 | Object combined[] = new Object[combinedLength]; | ||
447 | |||
448 | int cPos = 0; | ||
449 | if (!useInheritance) { | ||
450 | for (int i = 0; i < unmasked.getSize(); ++i) | ||
451 | combined[cPos++] = unmasked.get(i); | ||
452 | } | ||
453 | |||
454 | if (asComplementer) { | ||
455 | for (int i = 0; i < indices.length; ++i) | ||
456 | combined[cPos++] = masked.get(indices[i]); | ||
457 | } else { | ||
458 | ensureIndicesSorted(); | ||
459 | int mPos = 0; | ||
460 | for (int i = 0; i < masked.getSize(); ++i) | ||
461 | if (mPos < indicesSorted.length && i == indicesSorted[mPos]) | ||
462 | mPos++; | ||
463 | else | ||
464 | combined[cPos++] = masked.get(i); | ||
465 | } | ||
466 | |||
467 | return useInheritance ? Tuples.leftInheritanceTupleOf(unmasked, combined) : Tuples.flatTupleOf(combined); | ||
468 | } | ||
469 | |||
470 | @Override | ||
471 | public int hashCode() { | ||
472 | final int PRIME = 31; | ||
473 | int result = sourceWidth; | ||
474 | for (int i : indices) | ||
475 | result = PRIME * result + i; | ||
476 | return result; | ||
477 | } | ||
478 | |||
479 | @Override | ||
480 | public boolean equals(Object obj) { | ||
481 | if (this == obj) | ||
482 | return true; | ||
483 | if (obj == null) | ||
484 | return false; | ||
485 | if (getClass() != obj.getClass()) | ||
486 | return false; | ||
487 | final TupleMask other = (TupleMask) obj; | ||
488 | if (sourceWidth != other.sourceWidth) | ||
489 | return false; | ||
490 | if (indices.length != other.indices.length) | ||
491 | return false; | ||
492 | for (int k = 0; k < indices.length; k++) | ||
493 | if (indices[k] != other.indices[k]) | ||
494 | return false; | ||
495 | return true; | ||
496 | } | ||
497 | |||
498 | @Override | ||
499 | public String toString() { | ||
500 | StringBuilder s = new StringBuilder(); | ||
501 | s.append("M(" + sourceWidth + "->"); | ||
502 | for (int i : indices) { | ||
503 | s.append(i); | ||
504 | s.append(','); | ||
505 | } | ||
506 | s.append(')'); | ||
507 | return s.toString(); | ||
508 | } | ||
509 | |||
510 | /** | ||
511 | * Returns the size of the masked tuples described by this mask | ||
512 | * @since 1.7 | ||
513 | */ | ||
514 | public int getSize() { | ||
515 | return indices.length; | ||
516 | } | ||
517 | |||
518 | /** | ||
519 | * Returns the size of the original tuples handled by this mask | ||
520 | * @since 1.7 | ||
521 | */ | ||
522 | public int getSourceWidth() { | ||
523 | return sourceWidth; | ||
524 | } | ||
525 | |||
526 | |||
527 | /** | ||
528 | * @return true iff this mask is a no-op | ||
529 | * @since 2.0 | ||
530 | */ | ||
531 | public boolean isIdentity() { | ||
532 | // Contract: if identity mask, a specialized subclass is constructed instead | ||
533 | return false; | ||
534 | } | ||
535 | |||
536 | /** | ||
537 | * Transforms the given list by applying the mask and putting all results into a set; keeping only a single element | ||
538 | * in case of the mapping result in duplicate values. | ||
539 | * | ||
540 | * @since 1.7 | ||
541 | */ | ||
542 | public <T> Set<T> transformUnique(List<T> original) { | ||
543 | Set<T> signature = new HashSet<>(); | ||
544 | for (int i = 0; i < indices.length; ++i) | ||
545 | signature.add(original.get(indices[i])); | ||
546 | return signature; | ||
547 | } | ||
548 | |||
549 | /** | ||
550 | * @return the list of selected indices | ||
551 | * @since 2.1 | ||
552 | */ | ||
553 | public List<Integer> getIndicesAsList() { | ||
554 | List<Integer> result = new ArrayList<Integer>(indices.length); | ||
555 | for (int i = 0; i < indices.length; ++i) | ||
556 | result.add(indices[i]); | ||
557 | return result; | ||
558 | } | ||
559 | |||
560 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java new file mode 100644 index 00000000..5a0c79ff --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java | |||
@@ -0,0 +1,56 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | |||
14 | /** | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.7 | ||
17 | */ | ||
18 | public final class TupleMask0 extends TupleMask { | ||
19 | |||
20 | private final static int[] EMPTY_ARRAY = {}; | ||
21 | |||
22 | /** | ||
23 | * PRE: indices.length == 0 | ||
24 | */ | ||
25 | TupleMask0(int sourceWidth) { | ||
26 | super(EMPTY_ARRAY, sourceWidth, EMPTY_ARRAY, true); | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public <T> List<T> transform(List<T> original) { | ||
31 | return Collections.emptyList(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Tuple transform(ITuple original) { | ||
36 | return Tuples.staticArityFlatTupleOf(); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public TupleMask transform(TupleMask mask) { | ||
41 | return new TupleMask0(mask.sourceWidth); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) { | ||
46 | if (asComplementer) | ||
47 | return unmasked; | ||
48 | else | ||
49 | return super.combine(unmasked, masked, useInheritance, asComplementer); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public boolean isIdentity() { | ||
54 | return 0 == sourceWidth; | ||
55 | } | ||
56 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java new file mode 100644 index 00000000..62746587 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java | |||
@@ -0,0 +1,51 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public final class TupleMaskIdentity extends TupleMask { | ||
18 | |||
19 | TupleMaskIdentity(int sourceWidth) { | ||
20 | this(constructLinearSequence(sourceWidth), sourceWidth); | ||
21 | } | ||
22 | TupleMaskIdentity(int[] indices, int sourceWidth) { | ||
23 | super(indices, sourceWidth, indices, true); | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public <T> List<T> transform(List<T> original) { | ||
28 | return original; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public Tuple transform(ITuple original) { | ||
33 | return original.toImmutable(); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public TupleMask transform(TupleMask mask) { | ||
38 | return mask; | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public Tuple revertFrom(ITuple masked) { | ||
43 | return masked.toImmutable(); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public boolean isIdentity() { | ||
48 | return true; | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java new file mode 100644 index 00000000..79193516 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.tuple; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; | ||
14 | |||
15 | /** | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | */ | ||
19 | public class TupleValueProvider implements IValueProvider { | ||
20 | |||
21 | final ITuple tuple; | ||
22 | final Map<String, Integer> indexMapping; | ||
23 | |||
24 | /** | ||
25 | * Wraps a tuple with an index mapping | ||
26 | * @param tuple | ||
27 | * @param indexMapping | ||
28 | */ | ||
29 | public TupleValueProvider(ITuple tuple, Map<String, Integer> indexMapping) { | ||
30 | super(); | ||
31 | this.tuple = tuple; | ||
32 | this.indexMapping = indexMapping; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Object getValue(String variableName) { | ||
37 | Integer index = indexMapping.get(variableName); | ||
38 | if (index == null) { | ||
39 | throw new IllegalArgumentException(String.format("Variable %s is not present in mapping.", variableName)); | ||
40 | } | ||
41 | Object value = tuple.get(index); | ||
42 | if (value == null) { | ||
43 | throw new IllegalArgumentException(String.format("Variable %s is not found using index %d.", variableName, index)); | ||
44 | } | ||
45 | return value; | ||
46 | } | ||
47 | |||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java new file mode 100644 index 00000000..5e41d7d8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java | |||
@@ -0,0 +1,157 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.tuple; | ||
10 | |||
11 | /** | ||
12 | * Common static factory utilities for tuples. | ||
13 | * | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public class Tuples { | ||
18 | |||
19 | private Tuples() { | ||
20 | // Empty utility class constructor | ||
21 | } | ||
22 | |||
23 | /** | ||
24 | * Creates a flat tuple consisting of the given elements. | ||
25 | * For low-arity tuples, specialized implementations | ||
26 | * (such as {@link FlatTuple2}) will be instantiated. | ||
27 | * | ||
28 | * <p> In case the exact arity is <i>statically</i> known, | ||
29 | * it may be more efficient for the client to instantiate | ||
30 | * the appropriate specialized implementation | ||
31 | * (via {@link #staticArityFlatTupleOf(Object, Object)} etc. | ||
32 | * or {@link #wideFlatTupleOf(Object...)}), | ||
33 | * instead of invoking this method. | ||
34 | * This method does a runtime arity check, and therefore | ||
35 | * also appropriate if the arity is determined at runtime. | ||
36 | */ | ||
37 | public static Tuple flatTupleOf(Object... elements) { | ||
38 | switch (elements.length) { | ||
39 | case 0: | ||
40 | return FlatTuple0.INSTANCE; | ||
41 | case 1: | ||
42 | return new FlatTuple1(elements[0]); | ||
43 | case 2: | ||
44 | return new FlatTuple2(elements[0], elements[1]); | ||
45 | case 3: | ||
46 | return new FlatTuple3(elements[0], elements[1], elements[2]); | ||
47 | case 4: | ||
48 | return new FlatTuple4(elements[0], elements[1], elements[2], elements[3]); | ||
49 | default: | ||
50 | return new FlatTuple(elements); | ||
51 | } | ||
52 | } | ||
53 | /** | ||
54 | * Creates a left inheritance tuple that extends an ancestor tuple | ||
55 | * by the given "local" elements. | ||
56 | * For locally low-arity tuples, specialized implementations | ||
57 | * (such as {@link LeftInheritanceTuple2}) will be instantiated. | ||
58 | * | ||
59 | * <p> In case the exact arity is <i>statically</i> known, | ||
60 | * it may be more efficient for the client to instantiate | ||
61 | * the appropriate specialized implementation | ||
62 | * (via {@link #staticArityLeftInheritanceTupleOf(Object, Object)} etc. | ||
63 | * or {@link #wideLeftInheritanceTupleOf(Object...)}), | ||
64 | * instead of invoking this method. | ||
65 | * This method does a runtime arity check, and therefore | ||
66 | * also appropriate if the arity is determined at runtime. | ||
67 | */ | ||
68 | public static Tuple leftInheritanceTupleOf(Tuple ancestor, Object... localElements) { | ||
69 | switch (localElements.length) { | ||
70 | case 0: | ||
71 | return ancestor; | ||
72 | case 1: | ||
73 | return new LeftInheritanceTuple1(ancestor, localElements[0]); | ||
74 | case 2: | ||
75 | return new LeftInheritanceTuple2(ancestor, localElements[0], localElements[1]); | ||
76 | case 3: | ||
77 | return new LeftInheritanceTuple3(ancestor, localElements[0], localElements[1], localElements[2]); | ||
78 | case 4: | ||
79 | return new LeftInheritanceTuple4(ancestor, localElements[0], localElements[1], localElements[2], localElements[3]); | ||
80 | default: | ||
81 | return new LeftInheritanceTuple(ancestor, localElements); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Creates a flat tuple consisting of no elements. | ||
87 | */ | ||
88 | public static Tuple staticArityFlatTupleOf() { | ||
89 | return FlatTuple0.INSTANCE; | ||
90 | } | ||
91 | /** | ||
92 | * Creates a flat tuple consisting of the given single element. | ||
93 | */ | ||
94 | public static Tuple staticArityFlatTupleOf(Object element) { | ||
95 | return new FlatTuple1(element); | ||
96 | } | ||
97 | /** | ||
98 | * Creates a flat tuple consisting of the given elements. | ||
99 | */ | ||
100 | public static Tuple staticArityFlatTupleOf(Object element0, Object element1) { | ||
101 | return new FlatTuple2(element0, element1); | ||
102 | } | ||
103 | /** | ||
104 | * Creates a flat tuple consisting of the given elements. | ||
105 | */ | ||
106 | public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2) { | ||
107 | return new FlatTuple3(element0, element1, element2); | ||
108 | } | ||
109 | /** | ||
110 | * Creates a flat tuple consisting of the given elements. | ||
111 | */ | ||
112 | public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2, Object element3) { | ||
113 | return new FlatTuple4(element0, element1, element2, element3); | ||
114 | } | ||
115 | /** | ||
116 | * Creates a flat tuple consisting of the given elements. | ||
117 | * <p> Invoke this only if it is statically known that the tuple will be wide. | ||
118 | * Otherwise, use {@link #flatTupleOf(Object...)}. | ||
119 | */ | ||
120 | public static Tuple wideFlatTupleOf(Object... elements) { | ||
121 | return new FlatTuple(elements); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Creates a left inheritance tuple consisting of the given single local element. | ||
126 | */ | ||
127 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element) { | ||
128 | return new LeftInheritanceTuple1(ancestor, element); | ||
129 | } | ||
130 | /** | ||
131 | * Creates a left inheritance tuple consisting of the given local elements. | ||
132 | */ | ||
133 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1) { | ||
134 | return new LeftInheritanceTuple2(ancestor, element0, element1); | ||
135 | } | ||
136 | /** | ||
137 | * Creates a left inheritance tuple consisting of the given local elements. | ||
138 | */ | ||
139 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2) { | ||
140 | return new LeftInheritanceTuple3(ancestor, element0, element1, element2); | ||
141 | } | ||
142 | /** | ||
143 | * Creates a left inheritance tuple consisting of the given local elements. | ||
144 | */ | ||
145 | public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2, Object element3) { | ||
146 | return new LeftInheritanceTuple4(ancestor, element0, element1, element2, element3); | ||
147 | } | ||
148 | /** | ||
149 | * Creates a left inheritance tuple consisting of the given local elements. | ||
150 | * <p> Invoke this only if it is statically known that the tuple will be wide. | ||
151 | * Otherwise, use {@link #leftInheritanceTupleOf(Tuple, Object...)}. | ||
152 | */ | ||
153 | public static Tuple wideLeftInheritanceTupleOf(Tuple ancestor, Object... elements) { | ||
154 | return new LeftInheritanceTuple(ancestor, elements); | ||
155 | } | ||
156 | |||
157 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java new file mode 100644 index 00000000..f683d544 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java | |||
@@ -0,0 +1,50 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
12 | |||
13 | /** | ||
14 | * This class provides a volatile tuple view with a given mask of a given tuple instance. If the masked tuple changes, | ||
15 | * the view updates as well. | ||
16 | * | ||
17 | * @author Zoltan Ujhelyi | ||
18 | * @since 1.7 | ||
19 | * | ||
20 | */ | ||
21 | public class VolatileMaskedTuple extends VolatileTuple { | ||
22 | |||
23 | protected final TupleMask mask; | ||
24 | protected ITuple source; | ||
25 | |||
26 | public VolatileMaskedTuple(ITuple source, TupleMask mask) { | ||
27 | this.source = source; | ||
28 | this.mask = mask; | ||
29 | } | ||
30 | |||
31 | public VolatileMaskedTuple(TupleMask mask) { | ||
32 | this(null, mask); | ||
33 | } | ||
34 | |||
35 | public void updateTuple(ITuple newSource) { | ||
36 | source = newSource; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Object get(int index) { | ||
41 | Preconditions.checkState(source != null, "Source tuple is not set."); | ||
42 | return mask.getValue(source, index); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getSize() { | ||
47 | return mask.getSize(); | ||
48 | } | ||
49 | |||
50 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java new file mode 100644 index 00000000..92306c6e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java | |||
@@ -0,0 +1,47 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.tuple; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
12 | |||
13 | /** | ||
14 | * A masked tuple implementation that allows modifying the backing tuple. | ||
15 | * @author Zoltan Ujhelyi | ||
16 | * @since 1.7 | ||
17 | * | ||
18 | */ | ||
19 | public class VolatileModifiableMaskedTuple extends VolatileMaskedTuple implements IModifiableTuple { | ||
20 | |||
21 | private IModifiableTuple modifiableTuple; | ||
22 | |||
23 | public VolatileModifiableMaskedTuple(IModifiableTuple source, TupleMask mask) { | ||
24 | super(source, mask); | ||
25 | modifiableTuple = source; | ||
26 | } | ||
27 | |||
28 | public VolatileModifiableMaskedTuple(TupleMask mask) { | ||
29 | this(null, mask); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public void updateTuple(ITuple newSource) { | ||
34 | Preconditions.checkArgument(newSource instanceof IModifiableTuple, "Provided tuple does not support updates"); | ||
35 | this.updateTuple((IModifiableTuple)newSource); | ||
36 | } | ||
37 | |||
38 | public void updateTuple(IModifiableTuple newSource) { | ||
39 | super.updateTuple(newSource); | ||
40 | modifiableTuple = newSource; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void set(int index, Object value) { | ||
45 | mask.set(modifiableTuple, index, value); | ||
46 | } | ||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java new file mode 100644 index 00000000..699105a5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java | |||
@@ -0,0 +1,47 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017 Zoltan Ujhelyi, IncQuery Labs | ||
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 | |||
10 | package tools.refinery.viatra.runtime.matchers.tuple; | ||
11 | |||
12 | /** | ||
13 | * Mutable tuple without explicit modification commands. In practical terms, the values stored in a volatile tuple can | ||
14 | * be changed without any notification. | ||
15 | * | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | * | ||
19 | */ | ||
20 | public abstract class VolatileTuple extends AbstractTuple { | ||
21 | |||
22 | @Override | ||
23 | public boolean equals(Object obj) { | ||
24 | if (this == obj) | ||
25 | return true; | ||
26 | if (obj == null) | ||
27 | return false; | ||
28 | if (!(obj instanceof ITuple)) | ||
29 | return false; | ||
30 | final ITuple other = (ITuple) obj; | ||
31 | return internalEquals(other); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public int hashCode() { | ||
36 | return doCalcHash(); | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * Creates an immutable tuple from the values stored in the tuple. The created tuple will not be updated when the | ||
41 | * current tuple changes. | ||
42 | */ | ||
43 | @Override | ||
44 | public Tuple toImmutable() { | ||
45 | return Tuples.flatTupleOf(getElements()); | ||
46 | } | ||
47 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java new file mode 100644 index 00000000..338990ab --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java | |||
@@ -0,0 +1,48 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * The degree of accuracy of a cardinality estimate | ||
13 | * @author Gabor Bergmann | ||
14 | * @since 2.1 | ||
15 | */ | ||
16 | public enum Accuracy { | ||
17 | EXACT_COUNT, | ||
18 | BEST_UPPER_BOUND, | ||
19 | BEST_LOWER_BOUND, | ||
20 | APPROXIMATION; | ||
21 | |||
22 | /** | ||
23 | * Partial order comparison. | ||
24 | */ | ||
25 | public boolean atLeastAsPreciseAs(Accuracy other) { | ||
26 | switch (this) { | ||
27 | case EXACT_COUNT: return true; | ||
28 | case APPROXIMATION: return APPROXIMATION == other; | ||
29 | case BEST_UPPER_BOUND: return BEST_UPPER_BOUND == other || APPROXIMATION == other; | ||
30 | case BEST_LOWER_BOUND: return BEST_LOWER_BOUND == other || APPROXIMATION == other; | ||
31 | default: throw new IllegalArgumentException(); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * @return another accuracy value that is anti-monotonic to this one, | ||
37 | * i.e. an accuracy that should be used in the denominator to obtain a fraction with this accuracy | ||
38 | */ | ||
39 | public Accuracy reciprocal() { | ||
40 | switch(this) { | ||
41 | case APPROXIMATION: return APPROXIMATION; | ||
42 | case BEST_UPPER_BOUND: return BEST_LOWER_BOUND; | ||
43 | case BEST_LOWER_BOUND: return BEST_UPPER_BOUND; | ||
44 | case EXACT_COUNT: return EXACT_COUNT; | ||
45 | default: throw new IllegalArgumentException(); | ||
46 | } | ||
47 | } | ||
48 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java new file mode 100644 index 00000000..1b09aec6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.util; | ||
11 | |||
12 | /** | ||
13 | * @author Gabor Bergmann | ||
14 | * @since 1.7 | ||
15 | * An instance of clearable pattern memory. | ||
16 | */ | ||
17 | public interface Clearable { | ||
18 | /** | ||
19 | * Clear all partial matchings stored in memory | ||
20 | * | ||
21 | */ | ||
22 | void clear(); | ||
23 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java new file mode 100644 index 00000000..590a1ec3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java | |||
@@ -0,0 +1,188 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.util; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | import java.util.TreeMap; | ||
16 | import java.util.function.Function; | ||
17 | |||
18 | /** | ||
19 | * Factory class used as an accessor to Collections implementations. | ||
20 | * @author istvanrath | ||
21 | */ | ||
22 | public final class CollectionsFactory | ||
23 | { | ||
24 | |||
25 | /** | ||
26 | * Instantiates a new empty map. | ||
27 | * @since 1.7 | ||
28 | */ | ||
29 | public static <K, V> Map<K, V> createMap() { | ||
30 | return FRAMEWORK.createMap(); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Instantiates a new map with the given initial contents. | ||
35 | * @since 1.7 | ||
36 | */ | ||
37 | public static <K, V> Map<K, V> createMap(Map<K, V> initial) { | ||
38 | return FRAMEWORK.createMap(initial); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * Instantiates a new tree map. | ||
43 | * @since 2.3 | ||
44 | */ | ||
45 | public static <K, V> TreeMap<K, V> createTreeMap() { | ||
46 | return FRAMEWORK.createTreeMap(); | ||
47 | } | ||
48 | |||
49 | /** | ||
50 | * Instantiates a new empty set. | ||
51 | * @since 1.7 | ||
52 | */ | ||
53 | public static <E> Set<E> createSet() { | ||
54 | return FRAMEWORK.createSet(); | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Instantiates a new set with the given initial contents. | ||
59 | * @since 1.7 | ||
60 | */ | ||
61 | public static <E> Set<E> createSet(Collection<E> initial) { | ||
62 | return FRAMEWORK.createSet(initial); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * Instantiates an empty set; the key parameter is used to allow using this as a method reference as a | ||
67 | * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. | ||
68 | * | ||
69 | * @param key | ||
70 | * the value of this parameter is ignored | ||
71 | * @since 2.0 | ||
72 | */ | ||
73 | public static <T> Set<T> emptySet(Object key) { | ||
74 | return FRAMEWORK.createSet(); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Instantiates a new empty multiset. | ||
79 | * @since 1.7 | ||
80 | */ | ||
81 | public static <T> IMultiset<T> createMultiset() { | ||
82 | return FRAMEWORK.createMultiset(); | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * Instantiates an empty multiset; the key parameter is used to allow using this as a method reference as a | ||
87 | * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. | ||
88 | * | ||
89 | * @param key | ||
90 | * the value of this parameter is ignored | ||
91 | * @since 2.0 | ||
92 | */ | ||
93 | public static <T> IMultiset<T> emptyMultiset(Object key) { | ||
94 | return FRAMEWORK.createMultiset(); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * Instantiates a new empty delta bag. | ||
99 | * @since 1.7 | ||
100 | */ | ||
101 | public static <T> IDeltaBag<T> createDeltaBag() { | ||
102 | return FRAMEWORK.createDeltaBag(); | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * Instantiates a new list that is optimized for registering observers / callbacks. | ||
107 | * @since 1.7 | ||
108 | */ | ||
109 | public static <O> List<O> createObserverList() { | ||
110 | return FRAMEWORK.createObserverList(); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Instantiates a size-optimized multimap from keys to sets of values. | ||
115 | * <p>For a single key, many values can be associated according to the given bucket semantics. | ||
116 | * <p>The keys and values are stored as type fromKeys resp. ofValues; | ||
117 | * currently Object.class and Long.class are supported. | ||
118 | * @since 2.0 | ||
119 | */ | ||
120 | public static <K, V> IMultiLookup<K, V> createMultiLookup( | ||
121 | Class<? super K> fromKeys, MemoryType toBuckets, Class<? super V> ofValues) { | ||
122 | return FRAMEWORK.createMultiLookup(fromKeys, toBuckets, ofValues); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * Instantiates a memory storing values. | ||
127 | * <p>For a single key, many values can be associated according to the given memory semantics. | ||
128 | * <p>The values are stored as type 'values'; | ||
129 | * currently Object.class and Long.class are supported. | ||
130 | * @since 2.0 | ||
131 | */ | ||
132 | public static <T> IMemory<T> createMemory( | ||
133 | Class<? super T> values, MemoryType memoryType) { | ||
134 | return FRAMEWORK.createMemory(values, memoryType); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * The type of {@link IMemory} | ||
139 | * @since 2.0 | ||
140 | * TODO add delta as type | ||
141 | */ | ||
142 | public enum MemoryType { | ||
143 | /** | ||
144 | * A single key-value pair is stored at most once | ||
145 | */ | ||
146 | SETS, | ||
147 | /** | ||
148 | * Duplicate key-value pairs allowed | ||
149 | */ | ||
150 | MULTISETS | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * The collections framework of the current configuration. | ||
155 | * @since 1.7 | ||
156 | */ | ||
157 | private static final ICollectionsFramework FRAMEWORK = new EclipseCollectionsFactory(); | ||
158 | |||
159 | /** | ||
160 | * Interface abstracting over a collections technology that provides custom collection implementations. | ||
161 | * @since 1.7 | ||
162 | */ | ||
163 | public static interface ICollectionsFramework { | ||
164 | |||
165 | public abstract <K,V> Map<K,V> createMap(); | ||
166 | public abstract <K,V> Map<K,V> createMap(Map<K,V> initial); | ||
167 | /** | ||
168 | * @since 2.3 | ||
169 | */ | ||
170 | public abstract <K, V> TreeMap<K, V> createTreeMap(); | ||
171 | public abstract <E> Set<E> createSet(); | ||
172 | public abstract <E> Set<E> createSet(Collection<E> initial); | ||
173 | public abstract <T> IMultiset<T> createMultiset(); | ||
174 | public abstract <T> IDeltaBag<T> createDeltaBag(); | ||
175 | public abstract <O> List<O> createObserverList(); | ||
176 | |||
177 | /** | ||
178 | * @since 2.0 | ||
179 | */ | ||
180 | public abstract <K, V> IMultiLookup<K, V> createMultiLookup( | ||
181 | Class<? super K> fromKeys, MemoryType toBuckets, Class<? super V> ofValues); | ||
182 | /** | ||
183 | * @since 2.0 | ||
184 | */ | ||
185 | public abstract <T> IMemory<T> createMemory(Class<? super T> values, MemoryType memoryType); | ||
186 | } | ||
187 | |||
188 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java new file mode 100644 index 00000000..88f7ec00 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * Indicates whether a propagated update event signals the insertion or deletion of an element | ||
13 | * | ||
14 | * @author Gabor Bergmann | ||
15 | */ | ||
16 | public enum Direction { | ||
17 | INSERT, DELETE; | ||
18 | |||
19 | /** | ||
20 | * @since 2.4 | ||
21 | */ | ||
22 | public Direction opposite() { | ||
23 | switch (this) { | ||
24 | case INSERT: | ||
25 | return DELETE; | ||
26 | default: | ||
27 | return INSERT; | ||
28 | } | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * @since 2.4 | ||
33 | */ | ||
34 | public char asSign() { | ||
35 | switch (this) { | ||
36 | case INSERT: | ||
37 | return '+'; | ||
38 | default: | ||
39 | return '-'; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Returns the direction that is the product of this direction and the other direction. | ||
45 | * | ||
46 | * DELETE x DELETE = INSERT | ||
47 | * DELETE x INSERT = DELETE | ||
48 | * INSERT x DELETE = DELETE | ||
49 | * INSERT x INSERT = INSERT | ||
50 | * @since 2.4 | ||
51 | */ | ||
52 | public Direction multiply(final Direction other) { | ||
53 | switch (this) { | ||
54 | case DELETE: | ||
55 | return other.opposite(); | ||
56 | default: | ||
57 | return other; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java new file mode 100644 index 00000000..e24b2448 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java | |||
@@ -0,0 +1,86 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann 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 | |||
10 | package tools.refinery.viatra.runtime.matchers.util; | ||
11 | |||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.BiConsumer; | ||
15 | |||
16 | import org.eclipse.collections.impl.map.mutable.primitive.ObjectIntHashMap; | ||
17 | |||
18 | /** | ||
19 | * Eclipse Collections-based multiset for tuples. Can contain duplicate occurrences of the same matching. | ||
20 | * | ||
21 | * <p>Inherits Eclipse Collections' Object-to-Int primitive hashmap and counts the number of occurrences of each value. | ||
22 | * Element is deleted if # of occurences drops to 0. | ||
23 | * | ||
24 | * @author Gabor Bergmann. | ||
25 | * @since 1.7 | ||
26 | * @noreference | ||
27 | */ | ||
28 | public abstract class EclipseCollectionsBagMemory<T> extends ObjectIntHashMap<T> implements IMemory<T> { | ||
29 | |||
30 | public EclipseCollectionsBagMemory() { | ||
31 | super(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public int getCount(T value) { | ||
36 | return super.getIfAbsent(value, 0); | ||
37 | } | ||
38 | @Override | ||
39 | public int getCountUnsafe(Object value) { | ||
40 | return super.getIfAbsent(value, 0); | ||
41 | } | ||
42 | @Override | ||
43 | public boolean containsNonZero(T value) { | ||
44 | return super.containsKey(value); | ||
45 | } | ||
46 | @Override | ||
47 | public boolean containsNonZeroUnsafe(Object value) { | ||
48 | return super.containsKey(value); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public void clearAllOf(T value) { | ||
53 | super.remove(value); | ||
54 | } | ||
55 | |||
56 | |||
57 | @Override | ||
58 | public Iterator<T> iterator() { | ||
59 | return super.keySet().iterator(); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public String toString() { | ||
64 | return "TM" + super.toString(); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public Set<T> distinctValues() { | ||
69 | return super.keySet(); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
74 | super.forEachKeyValue(entryConsumer::accept); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public int hashCode() { | ||
79 | return IMemoryView.hashCode(this); | ||
80 | } | ||
81 | @Override | ||
82 | public boolean equals(Object obj) { | ||
83 | return IMemoryView.equals(this, obj); | ||
84 | } | ||
85 | |||
86 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java new file mode 100644 index 00000000..94ec33cd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java | |||
@@ -0,0 +1,41 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * @author Gabor Bergmann | ||
13 | * @since 1.7 | ||
14 | */ | ||
15 | public class EclipseCollectionsDeltaBag<T> extends EclipseCollectionsBagMemory<T> implements IDeltaBag<T> { | ||
16 | |||
17 | @Override | ||
18 | public boolean addOne(T value) { | ||
19 | return addSigned(value, +1); | ||
20 | } | ||
21 | |||
22 | @Override | ||
23 | public boolean addSigned(T value, int count) { | ||
24 | int oldCount = super.getIfAbsent(value, 0); | ||
25 | int newCount = oldCount + count; | ||
26 | |||
27 | boolean becomesZero = newCount == 0; | ||
28 | if (becomesZero) | ||
29 | super.removeKey(value); | ||
30 | else | ||
31 | super.put(value, newCount); | ||
32 | |||
33 | return becomesZero || oldCount == 0; | ||
34 | } | ||
35 | |||
36 | |||
37 | @Override | ||
38 | public boolean removeOne(T value) { | ||
39 | return addSigned(value, -1); | ||
40 | } | ||
41 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java new file mode 100644 index 00000000..5a623c9b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java | |||
@@ -0,0 +1,159 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | import java.util.TreeMap; | ||
17 | |||
18 | import org.eclipse.collections.api.map.MutableMap; | ||
19 | import org.eclipse.collections.impl.factory.Maps; | ||
20 | import org.eclipse.collections.impl.factory.Sets; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.ICollectionsFramework; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * @since 1.7 | ||
27 | * @noreference This class is not intended to be referenced by clients. | ||
28 | */ | ||
29 | public class EclipseCollectionsFactory implements ICollectionsFramework { | ||
30 | |||
31 | @Override | ||
32 | public <K, V> Map<K, V> createMap() { | ||
33 | return Maps.mutable.empty(); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public <K, V> Map<K, V> createMap(Map<K, V> initial) { | ||
38 | MutableMap<K, V> result = Maps.mutable.ofInitialCapacity(initial.size()); | ||
39 | result.putAll(initial); | ||
40 | return result; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public <K, V> TreeMap<K, V> createTreeMap() { | ||
45 | // eclipse collections is doing the same | ||
46 | return new TreeMap<>(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public <E> Set<E> createSet() { | ||
51 | return Sets.mutable.empty(); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public <E> Set<E> createSet(Collection<E> initial) { | ||
56 | return Sets.mutable.ofAll(initial); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public <T> IMultiset<T> createMultiset() { | ||
61 | return new EclipseCollectionsMultiset<T>(); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public <T> IDeltaBag<T> createDeltaBag() { | ||
66 | return new EclipseCollectionsDeltaBag<T>(); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public <O> List<O> createObserverList() { | ||
71 | return new ArrayList<O>(1); // keep concurrent modification exceptions for error detection | ||
72 | // Lists.mutable.empty | ||
73 | |||
74 | } | ||
75 | |||
76 | @Override | ||
77 | @SuppressWarnings({ "unchecked", "rawtypes" }) | ||
78 | public <K, V> IMultiLookup<K, V> createMultiLookup( | ||
79 | Class<? super K> fromKeys, | ||
80 | MemoryType toBuckets, | ||
81 | Class<? super V> ofValues) | ||
82 | { | ||
83 | boolean longKeys = Long.class.equals(fromKeys); | ||
84 | boolean objectKeys = Object.class.equals(fromKeys); | ||
85 | if (! (longKeys || objectKeys)) throw new IllegalArgumentException(fromKeys.getName()); | ||
86 | boolean longValues = Long.class.equals(ofValues); | ||
87 | boolean objectValues = Object.class.equals(ofValues); | ||
88 | if (! (longValues || objectValues)) throw new IllegalArgumentException(ofValues.getName()); | ||
89 | |||
90 | if (longKeys) { // K == java.lang.Long | ||
91 | if (longValues) { // V == java.lang.Long | ||
92 | switch(toBuckets) { | ||
93 | case MULTISETS: | ||
94 | return (IMultiLookup<K, V>) new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfLongs(); | ||
95 | case SETS: | ||
96 | return (IMultiLookup<K, V>) new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfLongs(); | ||
97 | default: | ||
98 | throw new IllegalArgumentException(toBuckets.toString()); | ||
99 | } | ||
100 | } else { // objectValues | ||
101 | switch(toBuckets) { | ||
102 | case MULTISETS: | ||
103 | return new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfObjects(); | ||
104 | case SETS: | ||
105 | return new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfObjects(); | ||
106 | default: | ||
107 | throw new IllegalArgumentException(toBuckets.toString()); | ||
108 | } | ||
109 | } | ||
110 | } else { // objectKeys | ||
111 | if (longValues) { // V == java.lang.Long | ||
112 | switch(toBuckets) { | ||
113 | case MULTISETS: | ||
114 | return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfLongs(); | ||
115 | case SETS: | ||
116 | return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfLongs(); | ||
117 | default: | ||
118 | throw new IllegalArgumentException(toBuckets.toString()); | ||
119 | } | ||
120 | } else { // objectValues | ||
121 | switch(toBuckets) { | ||
122 | case MULTISETS: | ||
123 | return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfObjects(); | ||
124 | case SETS: | ||
125 | return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfObjects(); | ||
126 | default: | ||
127 | throw new IllegalArgumentException(toBuckets.toString()); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | @Override | ||
134 | @SuppressWarnings("unchecked") | ||
135 | public <T> IMemory<T> createMemory(Class<? super T> values, MemoryType memoryType) { | ||
136 | if (Long.class.equals(values)) { // T == java.lang.Long | ||
137 | switch(memoryType) { | ||
138 | case MULTISETS: | ||
139 | return (IMemory<T>) new EclipseCollectionsLongMultiset(); | ||
140 | case SETS: | ||
141 | return (IMemory<T>) new EclipseCollectionsLongSetMemory(); | ||
142 | default: | ||
143 | throw new IllegalArgumentException(memoryType.toString()); | ||
144 | } | ||
145 | } else { // objectValues | ||
146 | switch(memoryType) { | ||
147 | case MULTISETS: | ||
148 | return new EclipseCollectionsMultiset<>(); | ||
149 | case SETS: | ||
150 | return new EclipseCollectionsSetMemory<>(); | ||
151 | default: | ||
152 | throw new IllegalArgumentException(memoryType.toString()); | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | |||
158 | |||
159 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java new file mode 100644 index 00000000..88773c5d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java | |||
@@ -0,0 +1,150 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Set; | ||
13 | import java.util.function.BiConsumer; | ||
14 | |||
15 | import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; | ||
16 | |||
17 | /** | ||
18 | * @author Gabor Bergmann | ||
19 | * @since 2.0 | ||
20 | * <p> TODO refactor common methods with {@link EclipseCollectionsMultiset} | ||
21 | * <p> TODO refactor into LongBagMemory etc. | ||
22 | */ | ||
23 | public class EclipseCollectionsLongMultiset extends LongIntHashMap implements IMultiset<Long> { | ||
24 | |||
25 | @Override | ||
26 | public boolean addOne(Long value) { | ||
27 | int oldCount = super.getIfAbsent(value, 0); | ||
28 | |||
29 | super.put(value, oldCount + 1); | ||
30 | |||
31 | return oldCount == 0; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public boolean addSigned(Long value, int count) { | ||
36 | int oldCount = super.getIfAbsent(value, 0); | ||
37 | int newCount = oldCount + count; | ||
38 | |||
39 | boolean becomesZero = newCount == 0; | ||
40 | if (newCount < 0) | ||
41 | throw new IllegalStateException(String.format( | ||
42 | "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", | ||
43 | count, value, newCount, this)); | ||
44 | else if (becomesZero) | ||
45 | super.removeKey(value); | ||
46 | else // (newCount > 0) | ||
47 | super.put(value, newCount); | ||
48 | |||
49 | return becomesZero || oldCount == 0; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public boolean removeOne(Long value) { | ||
54 | return removeOneInternal(value, true); | ||
55 | } | ||
56 | /** | ||
57 | * @since 2.3 | ||
58 | */ | ||
59 | @Override | ||
60 | public boolean removeOneOrNop(Long value) { | ||
61 | return removeOneInternal(value, false); | ||
62 | } | ||
63 | |||
64 | |||
65 | /** | ||
66 | * @since 2.3 | ||
67 | */ | ||
68 | protected boolean removeOneInternal(Long value, boolean throwIfImpossible) { | ||
69 | int oldCount = super.getIfAbsent(value, 0); | ||
70 | if (oldCount == 0) { | ||
71 | if (throwIfImpossible) throw new IllegalStateException(String.format( | ||
72 | "Cannot remove value '%s' that is not contained in %s", | ||
73 | value, this)); | ||
74 | else return false; | ||
75 | } | ||
76 | |||
77 | int rest = oldCount - 1; | ||
78 | boolean empty = rest == 0; | ||
79 | |||
80 | if (!empty) { | ||
81 | super.put(value, rest); | ||
82 | } else { | ||
83 | super.remove(value); | ||
84 | } | ||
85 | |||
86 | return empty; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public void clearAllOf(Long value) { | ||
91 | super.remove(value); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public int getCount(Long value) { | ||
96 | return super.getIfAbsent(value, 0); | ||
97 | } | ||
98 | @Override | ||
99 | public int getCountUnsafe(Object value) { | ||
100 | return value instanceof Long ? getCount((Long) value) : 0; | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public boolean containsNonZero(Long value) { | ||
105 | return super.containsKey(value); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public boolean containsNonZeroUnsafe(Object value) { | ||
110 | return value instanceof Long && containsNonZero((Long) value); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public Iterator<Long> iterator() { | ||
115 | return EclipseCollectionsLongSetMemory.iteratorOf(super.keySet()); | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public boolean addPositive(Long value, int count) { | ||
120 | if (count < 0) { | ||
121 | throw new IllegalArgumentException("The count value must be positive!"); | ||
122 | } | ||
123 | |||
124 | int oldCount = super.getIfAbsent(value, 0); | ||
125 | |||
126 | super.put(value, oldCount + count); | ||
127 | |||
128 | return oldCount == 0; | ||
129 | } | ||
130 | |||
131 | @Override | ||
132 | public Set<Long> distinctValues() { | ||
133 | return new EclipseCollectionsLongSetMemory.SetWrapper(super.keySet()); | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public void forEachEntryWithMultiplicities(BiConsumer<Long, Integer> entryConsumer) { | ||
138 | super.forEachKeyValue(entryConsumer::accept); | ||
139 | } | ||
140 | |||
141 | @Override | ||
142 | public int hashCode() { | ||
143 | return IMemoryView.hashCode(this); | ||
144 | } | ||
145 | @Override | ||
146 | public boolean equals(Object obj) { | ||
147 | return IMemoryView.equals(this, obj); | ||
148 | } | ||
149 | |||
150 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java new file mode 100644 index 00000000..fceb54fc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java | |||
@@ -0,0 +1,212 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import org.eclipse.collections.api.LongIterable; | ||
16 | import org.eclipse.collections.api.iterator.LongIterator; | ||
17 | import org.eclipse.collections.api.set.primitive.LongSet; | ||
18 | import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 2.0 | ||
23 | */ | ||
24 | public class EclipseCollectionsLongSetMemory extends LongHashSet implements ISetMemory<Long> { | ||
25 | |||
26 | @Override | ||
27 | public boolean addOne(Long value) { | ||
28 | return super.add(value); | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public boolean addSigned(Long value, int count) { | ||
33 | if (count == 1) return addOne(value); | ||
34 | else if (count == -1) return removeOne(value); | ||
35 | else throw new IllegalStateException(); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public boolean removeOne(Long value) { | ||
40 | // Kept for binary compatibility | ||
41 | return ISetMemory.super.removeOne(value); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * @since 2.3 | ||
46 | */ | ||
47 | @Override | ||
48 | public boolean removeOneOrNop(Long value) { | ||
49 | return super.remove(value); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void clearAllOf(Long value) { | ||
54 | super.remove(value); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public int getCount(Long value) { | ||
59 | return super.contains(value) ? 1 : 0; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public int getCountUnsafe(Object value) { | ||
64 | return value instanceof Long ? getCount((Long) value) : 0; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public boolean containsNonZero(Long value) { | ||
69 | return super.contains(value); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean containsNonZeroUnsafe(Object value) { | ||
74 | return value instanceof Long && containsNonZero((Long) value); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public Iterator<Long> iterator() { | ||
79 | return iteratorOf(this); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public Set<Long> distinctValues() { | ||
84 | return new SetWrapper(this); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public boolean isEmpty() { | ||
89 | return super.isEmpty(); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Helper for iterating a LongIterable | ||
94 | */ | ||
95 | public static Iterator<Long> iteratorOf(LongIterable wrapped) { | ||
96 | return new Iterator<Long>() { | ||
97 | LongIterator longIterator = wrapped.longIterator(); | ||
98 | |||
99 | @Override | ||
100 | public boolean hasNext() { | ||
101 | return longIterator.hasNext(); | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public Long next() { | ||
106 | return longIterator.next(); | ||
107 | } | ||
108 | }; | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | public int hashCode() { | ||
113 | return IMemoryView.hashCode(this); | ||
114 | } | ||
115 | @Override | ||
116 | public boolean equals(Object obj) { | ||
117 | return IMemoryView.equals(this, obj); | ||
118 | } | ||
119 | |||
120 | |||
121 | /** | ||
122 | * Helper that presents a primitive collection as a Set view | ||
123 | * @author Gabor Bergmann | ||
124 | */ | ||
125 | public static final class SetWrapper implements Set<Long> { | ||
126 | private LongSet wrapped; | ||
127 | |||
128 | /** | ||
129 | * @param wrapped | ||
130 | */ | ||
131 | public SetWrapper(LongSet wrapped) { | ||
132 | this.wrapped = wrapped; | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public int size() { | ||
137 | return wrapped.size(); | ||
138 | } | ||
139 | |||
140 | @Override | ||
141 | public boolean isEmpty() { | ||
142 | return wrapped.isEmpty(); | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public boolean contains(Object o) { | ||
147 | return o instanceof Long && wrapped.contains((Long)o); | ||
148 | } | ||
149 | |||
150 | @Override | ||
151 | public Iterator<Long> iterator() { | ||
152 | return iteratorOf(wrapped); | ||
153 | } | ||
154 | |||
155 | @Override | ||
156 | public boolean containsAll(Collection<?> c) { | ||
157 | for (Object object : c) { | ||
158 | if (contains(object)) | ||
159 | return true; | ||
160 | } | ||
161 | return false; | ||
162 | } | ||
163 | |||
164 | @Override | ||
165 | public Object[] toArray() { | ||
166 | return toArray(new Long[wrapped.size()]); | ||
167 | } | ||
168 | |||
169 | @Override | ||
170 | @SuppressWarnings("unchecked") | ||
171 | public <T> T[] toArray(T[] a) { | ||
172 | int k = 0; | ||
173 | LongIterator iterator = wrapped.longIterator(); | ||
174 | while (iterator.hasNext()) | ||
175 | a[k++] = (T) Long.valueOf(iterator.next()); | ||
176 | return a; | ||
177 | } | ||
178 | |||
179 | @Override | ||
180 | public boolean add(Long e) { | ||
181 | throw new UnsupportedOperationException(); | ||
182 | } | ||
183 | |||
184 | @Override | ||
185 | public boolean remove(Object o) { | ||
186 | throw new UnsupportedOperationException(); | ||
187 | } | ||
188 | |||
189 | @Override | ||
190 | public boolean addAll(Collection<? extends Long> c) { | ||
191 | throw new UnsupportedOperationException(); | ||
192 | } | ||
193 | |||
194 | @Override | ||
195 | public boolean retainAll(Collection<?> c) { | ||
196 | throw new UnsupportedOperationException(); | ||
197 | } | ||
198 | |||
199 | @Override | ||
200 | public boolean removeAll(Collection<?> c) { | ||
201 | throw new UnsupportedOperationException(); | ||
202 | } | ||
203 | |||
204 | @Override | ||
205 | public void clear() { | ||
206 | throw new UnsupportedOperationException(); | ||
207 | } | ||
208 | |||
209 | |||
210 | } | ||
211 | |||
212 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java new file mode 100644 index 00000000..394135c9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java | |||
@@ -0,0 +1,226 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import org.eclipse.collections.impl.map.mutable.UnifiedMap; | ||
12 | import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedMultiset; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; | ||
15 | |||
16 | import java.util.Set; | ||
17 | import java.util.stream.Stream; | ||
18 | |||
19 | |||
20 | |||
21 | /** | ||
22 | * Eclipse Collections-based realizations of {@link IMultiLookup} | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | * @since 2.0 | ||
26 | */ | ||
27 | class EclipseCollectionsMultiLookup { | ||
28 | |||
29 | private EclipseCollectionsMultiLookup() {/* Hidden utility class constructor */} | ||
30 | |||
31 | private static class MarkedSetImpl<Value> extends EclipseCollectionsSetMemory<Value> implements MarkedMemory.MarkedSet<Value> {} | ||
32 | private static class MarkedMultisetImpl<Value> extends EclipseCollectionsMultiset<Value> implements MarkedMemory.MarkedMultiset<Value> {} | ||
33 | private static class MarkedLongSetImpl extends EclipseCollectionsLongSetMemory implements MarkedMemory.MarkedSet<Long> {} | ||
34 | private static class MarkedLongMultisetImpl extends EclipseCollectionsLongMultiset implements MarkedMemory.MarkedMultiset<Long> {} | ||
35 | |||
36 | public abstract static class FromObjects<Key, Value, Bucket extends MarkedMemory<Value>> | ||
37 | extends UnifiedMap<Key, Object> implements IMultiLookupAbstract<Key, Value, Bucket> { | ||
38 | |||
39 | @Override | ||
40 | public boolean equals(Object obj) { | ||
41 | return IMultiLookup.equals(this, obj); | ||
42 | } | ||
43 | @Override | ||
44 | public int hashCode() { | ||
45 | return IMultiLookup.hashCode(this); | ||
46 | } | ||
47 | |||
48 | |||
49 | @Override | ||
50 | public Object lowLevelPutIfAbsent(Key key, Value value) { | ||
51 | return super.putIfAbsent(key, value); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public Object lowLevelGet(Key key) { | ||
56 | return super.get(key); | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public Object lowLevelGetUnsafe(Object key) { | ||
61 | return super.get(key); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public Object lowLevelRemove(Key key) { | ||
66 | return super.remove(key); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public void lowLevelPut(Key key, Object valueOrBucket) { | ||
71 | super.put(key, valueOrBucket); | ||
72 | } | ||
73 | @Override | ||
74 | public Iterable<Object> lowLevelValues() { | ||
75 | return super.values(); | ||
76 | } | ||
77 | @Override | ||
78 | public Set<Key> lowLevelKeySet() { | ||
79 | return super.keySet(); | ||
80 | } | ||
81 | @Override | ||
82 | public int lowLevelSize() { | ||
83 | return super.size(); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public Stream<Key> distinctKeysStream() { | ||
88 | // may be more efficient than the default spliterator | ||
89 | return super.keySet().stream(); | ||
90 | } | ||
91 | |||
92 | public abstract static class ToSets<Key, Value> extends FromObjects<Key, Value, MarkedSet<Value>> | ||
93 | implements IMultiLookupAbstract.ToSetsAbstract<Key, Value> | ||
94 | { | ||
95 | public static class OfObjects<Key, Value> extends ToSets<Key, Value> { | ||
96 | @Override | ||
97 | public MarkedSet<Value> createMarkedSet() { | ||
98 | return new MarkedSetImpl<Value>(); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | public static class OfLongs<Key> extends ToSets<Key, Long> { | ||
103 | @Override | ||
104 | public MarkedSet<Long> createMarkedSet() { | ||
105 | return new MarkedLongSetImpl(); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | } | ||
110 | |||
111 | public abstract static class ToMultisets<Key, Value> extends FromObjects<Key, Value, MarkedMultiset<Value>> | ||
112 | implements IMultiLookupAbstract.ToMultisetsAbstract<Key, Value> | ||
113 | { | ||
114 | public static class OfObjects<Key, Value> extends ToMultisets<Key, Value> { | ||
115 | @Override | ||
116 | public MarkedMultiset<Value> createMarkedMultiset() { | ||
117 | return new MarkedMultisetImpl<Value>(); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | public static class OfLongs<Key> extends ToMultisets<Key, Long> { | ||
122 | @Override | ||
123 | public MarkedMultiset<Long> createMarkedMultiset() { | ||
124 | return new MarkedLongMultisetImpl(); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | } | ||
129 | |||
130 | } | ||
131 | |||
132 | public abstract static class FromLongs<Value, Bucket extends MarkedMemory<Value>> | ||
133 | extends LongObjectHashMap<Object> implements IMultiLookupAbstract<Long, Value, Bucket> { | ||
134 | |||
135 | @Override | ||
136 | public boolean equals(Object obj) { | ||
137 | return IMultiLookup.equals(this, obj); | ||
138 | } | ||
139 | @Override | ||
140 | public int hashCode() { | ||
141 | return IMultiLookup.hashCode(this); | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public Object lowLevelPutIfAbsent(Long key, Value value) { | ||
146 | Object old = super.get(key); | ||
147 | if (old == null) super.put(key, value); | ||
148 | return old; | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public Object lowLevelGet(Long key) { | ||
153 | return super.get(key); | ||
154 | } | ||
155 | |||
156 | @Override | ||
157 | public Object lowLevelGetUnsafe(Object key) { | ||
158 | return key instanceof Long ? super.get((Long)key) : null; | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public Object lowLevelRemove(Long key) { | ||
163 | return super.remove(key); | ||
164 | } | ||
165 | |||
166 | @Override | ||
167 | public void lowLevelPut(Long key, Object valueOrBucket) { | ||
168 | super.put(key, valueOrBucket); | ||
169 | } | ||
170 | @Override | ||
171 | public Iterable<Object> lowLevelValues() { | ||
172 | return super.values(); | ||
173 | } | ||
174 | @Override | ||
175 | public int lowLevelSize() { | ||
176 | return super.size(); | ||
177 | } | ||
178 | @Override | ||
179 | public Iterable<Long> lowLevelKeySet() { | ||
180 | return () -> EclipseCollectionsLongSetMemory.iteratorOf(FromLongs.super.keysView()); | ||
181 | } | ||
182 | |||
183 | public abstract static class ToSets<Value> extends FromLongs<Value, MarkedSet<Value>> | ||
184 | implements IMultiLookupAbstract.ToSetsAbstract<Long, Value> | ||
185 | { | ||
186 | public static class OfObjects<Value> extends ToSets<Value> { | ||
187 | @Override | ||
188 | public MarkedSet<Value> createMarkedSet() { | ||
189 | return new MarkedSetImpl<Value>(); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | public static class OfLongs extends ToSets<Long> { | ||
194 | @Override | ||
195 | public MarkedSet<Long> createMarkedSet() { | ||
196 | return new MarkedLongSetImpl(); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | } | ||
201 | |||
202 | public abstract static class ToMultisets<Value> extends FromLongs<Value, MarkedMultiset<Value>> | ||
203 | implements IMultiLookupAbstract.ToMultisetsAbstract<Long, Value> | ||
204 | { | ||
205 | public static class OfObjects<Value> extends ToMultisets<Value> { | ||
206 | @Override | ||
207 | public MarkedMultiset<Value> createMarkedMultiset() { | ||
208 | return new MarkedMultisetImpl<Value>(); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | public static class OfLongs extends ToMultisets<Long> { | ||
213 | @Override | ||
214 | public MarkedMultiset<Long> createMarkedMultiset() { | ||
215 | return new MarkedLongMultisetImpl(); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | } | ||
220 | |||
221 | } | ||
222 | |||
223 | |||
224 | } | ||
225 | |||
226 | |||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java new file mode 100644 index 00000000..46977c8b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java | |||
@@ -0,0 +1,93 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * @author Gabor Bergmann | ||
13 | * @since 1.7 | ||
14 | */ | ||
15 | public class EclipseCollectionsMultiset<T> extends EclipseCollectionsBagMemory<T> implements IMultiset<T> { | ||
16 | |||
17 | @Override | ||
18 | public boolean addOne(T value) { | ||
19 | int oldCount = super.getIfAbsent(value, 0); | ||
20 | |||
21 | super.put(value, oldCount + 1); | ||
22 | |||
23 | return oldCount == 0; | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public boolean addPositive(T value, int count) { | ||
28 | if (count < 0) { | ||
29 | throw new IllegalArgumentException("The count value must be positive!"); | ||
30 | } | ||
31 | |||
32 | int oldCount = super.getIfAbsent(value, 0); | ||
33 | |||
34 | super.put(value, oldCount + count); | ||
35 | |||
36 | return oldCount == 0; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public boolean addSigned(T value, int count) { | ||
41 | int oldCount = super.getIfAbsent(value, 0); | ||
42 | int newCount = oldCount + count; | ||
43 | |||
44 | boolean becomesZero = newCount == 0; | ||
45 | if (newCount < 0) | ||
46 | throw new IllegalStateException(String.format( | ||
47 | "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", | ||
48 | count, value, newCount, this)); | ||
49 | else if (becomesZero) | ||
50 | super.removeKey(value); | ||
51 | else // (newCount > 0) | ||
52 | super.put(value, newCount); | ||
53 | |||
54 | return becomesZero || oldCount == 0; | ||
55 | } | ||
56 | |||
57 | |||
58 | @Override | ||
59 | public boolean removeOne(T value) { | ||
60 | return removeOneInternal(value, true); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public boolean removeOneOrNop(T value) { | ||
65 | return removeOneInternal(value, false); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * @since 2.3 | ||
70 | */ | ||
71 | protected boolean removeOneInternal(T value, boolean throwIfImpossible) { | ||
72 | int oldCount = super.getIfAbsent(value, 0); | ||
73 | if (oldCount == 0) { | ||
74 | if (throwIfImpossible) throw new IllegalStateException(String.format( | ||
75 | "Cannot remove value '%s' that is not contained in %s", | ||
76 | value, this)); | ||
77 | else return false; | ||
78 | } | ||
79 | |||
80 | int rest = oldCount - 1; | ||
81 | boolean empty = rest == 0; | ||
82 | |||
83 | if (!empty) { | ||
84 | super.put(value, rest); | ||
85 | } else { | ||
86 | super.remove(value); | ||
87 | } | ||
88 | |||
89 | return empty; | ||
90 | } | ||
91 | |||
92 | |||
93 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java new file mode 100644 index 00000000..92f65246 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import org.eclipse.collections.impl.set.mutable.UnifiedSet; | ||
14 | |||
15 | /** | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 2.0 | ||
18 | */ | ||
19 | public class EclipseCollectionsSetMemory<Value> extends UnifiedSet<Value> implements ISetMemory<Value> { | ||
20 | @Override | ||
21 | public int getCount(Value value) { | ||
22 | return super.contains(value) ? 1 : 0; | ||
23 | } | ||
24 | @Override | ||
25 | public int getCountUnsafe(Object value) { | ||
26 | return super.contains(value) ? 1 : 0; | ||
27 | } | ||
28 | @Override | ||
29 | public boolean containsNonZero(Value value) { | ||
30 | return super.contains(value); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public boolean containsNonZeroUnsafe(Object value) { | ||
35 | return super.contains(value); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public boolean addOne(Value value) { | ||
40 | return super.add(value); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public boolean addSigned(Value value, int count) { | ||
45 | if (count == 1) return addOne(value); | ||
46 | else if (count == -1) return removeOne(value); | ||
47 | else throw new IllegalStateException(); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public boolean removeOne(Value value) { | ||
52 | // Kept for binary compatibility | ||
53 | return ISetMemory.super.removeOne(value); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public boolean removeOneOrNop(Value value) { | ||
58 | return super.remove(value); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public void clearAllOf(Value value) { | ||
63 | super.remove(value); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public Set<Value> distinctValues() { | ||
68 | return this; | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Value theContainedVersionOf(Value value) { | ||
73 | return super.get(value); | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | @SuppressWarnings("unchecked") | ||
78 | public Value theContainedVersionOfUnsafe(Object value) { | ||
79 | if (super.contains(value)) | ||
80 | return super.get((Value)value); | ||
81 | else return null; | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public int hashCode() { | ||
86 | return IMemoryView.hashCode(this); | ||
87 | } | ||
88 | @Override | ||
89 | public boolean equals(Object obj) { | ||
90 | return IMemoryView.equals(this, obj); | ||
91 | } | ||
92 | |||
93 | |||
94 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java new file mode 100644 index 00000000..a17b3a3f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java | |||
@@ -0,0 +1,93 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | /** | ||
16 | * A singleton immutable empty memory. | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 2.0 | ||
19 | * | ||
20 | */ | ||
21 | public class EmptyMemory<T> implements IMemoryView<T> { | ||
22 | |||
23 | @SuppressWarnings("rawtypes") | ||
24 | private static final EmptyMemory INSTANCE = new EmptyMemory(); | ||
25 | |||
26 | @SuppressWarnings("unchecked") | ||
27 | public static <T> EmptyMemory<T> instance() { | ||
28 | return INSTANCE; | ||
29 | } | ||
30 | |||
31 | |||
32 | |||
33 | /** | ||
34 | * Singleton; hidden constructor | ||
35 | */ | ||
36 | private EmptyMemory() { | ||
37 | super(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Iterator<T> iterator() { | ||
42 | return Collections.<T>emptySet().iterator(); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getCount(T value) { | ||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public int getCountUnsafe(Object value) { | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public boolean containsNonZero(T value) { | ||
57 | return false; | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public boolean containsNonZeroUnsafe(Object value) { | ||
62 | return false; | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public int size() { | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public boolean isEmpty() { | ||
72 | return true; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public Set<T> distinctValues() { | ||
77 | return Collections.emptySet(); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public int hashCode() { | ||
82 | return IMemoryView.hashCode(this); | ||
83 | } | ||
84 | @Override | ||
85 | public boolean equals(Object obj) { | ||
86 | return IMemoryView.equals(this, obj); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public String toString() { | ||
91 | return "{}"; | ||
92 | } | ||
93 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java new file mode 100644 index 00000000..8c2e54ad --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.function.Supplier; | ||
12 | |||
13 | /** | ||
14 | * A cache is a simple key-value pair that stores calculated values for specific key objects | ||
15 | * | ||
16 | * <p> | ||
17 | * <b>NOTE</b> These caches are not expected to be used outside query backend implementations | ||
18 | * | ||
19 | * @author Zoltan Ujhelyi | ||
20 | * @since 1.7 | ||
21 | * @noreference This interface is not intended to be referenced by clients. | ||
22 | */ | ||
23 | public interface ICache { | ||
24 | |||
25 | /** | ||
26 | * Return a selected value for the key object. If the value is not available in the cache yet, the given provider is | ||
27 | * called once | ||
28 | * @since 2.0 | ||
29 | */ | ||
30 | <T> T getValue(Object key, Class<? extends T> clazz, Supplier<T> valueProvider); | ||
31 | |||
32 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java new file mode 100644 index 00000000..99a4cb3b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * An {@link IMemory} that represents the difference between two states of a set or {@link IMultiset}, and therefore | ||
13 | * may contain values with a negative multiplicity. | ||
14 | * | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.7 | ||
17 | */ | ||
18 | public interface IDeltaBag<T> extends IMemory<T> { | ||
19 | |||
20 | @Override | ||
21 | default boolean removeOneOrNop(T value) { | ||
22 | // makes no difference for delta bags | ||
23 | return removeOne(value); | ||
24 | } | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java new file mode 100644 index 00000000..ea788e53 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java | |||
@@ -0,0 +1,81 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * A memory containing a positive or negative number of equal() copies for some values. | ||
13 | * During iterations, each distinct value is iterated only once. | ||
14 | * | ||
15 | * <p> Refined by: <ul> | ||
16 | * <li>{@link IMultiset}, which always contains values with a nonnegative multiplicity. </li> | ||
17 | * <li>{@link IDeltaBag}, which may contain values with negative multiplicity. </li> | ||
18 | * <li>{@link ISetMemory}, which is just a set (allowed multiplicities: 0 and 1). </li> | ||
19 | * </ul> | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 1.7 | ||
23 | * @noimplement This interface is not intended to be implemented by clients. | ||
24 | */ | ||
25 | public interface IMemory<T> extends IMemoryView<T>, Clearable { | ||
26 | |||
27 | /** | ||
28 | * Adds one value occurrence to the memory. | ||
29 | * | ||
30 | * @return true if the tuple was not present before in the memory, or | ||
31 | * (in case of {@link IDeltaBag}) is no longer present in the memory | ||
32 | */ | ||
33 | boolean addOne(T value); | ||
34 | |||
35 | /** | ||
36 | * Adds the given number of occurrences to the memory. The count value may or may not be negative. | ||
37 | * <p> Precondition if {@link IMultiset}: at least the given amount of occurrences exist, if count is negative. | ||
38 | * <p> Precondition if {@link ISetMemory}: count is +1 or -1, the latter is only allowed if the set contains the value. | ||
39 | * | ||
40 | * @param count | ||
41 | * the number of occurrences | ||
42 | * @return true if the tuple was not present before in the memory, or is no longer present in the memory | ||
43 | * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and the number of occurrences in the memory would underflow to negative | ||
44 | */ | ||
45 | boolean addSigned(T value, int count); | ||
46 | |||
47 | /** | ||
48 | * Removes one occurrence of the given value from the memory. | ||
49 | * <p> Precondition if {@link IMultiset} or {@link ISetMemory}: the value must have a positive amount of occurrences in the memory. | ||
50 | * | ||
51 | * @return true if this was the the last occurrence of the value, or | ||
52 | * (in case of {@link IDeltaBag}) is the first negative occurrence of the value | ||
53 | * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory | ||
54 | */ | ||
55 | boolean removeOne(T value); | ||
56 | |||
57 | /** | ||
58 | * Removes one occurrence of the given value from the memory, if possible. | ||
59 | * | ||
60 | * <p> Memory is unchanged and false is returned if | ||
61 | * {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory | ||
62 | * | ||
63 | * @return true if this was the the last occurrence of the value, or | ||
64 | * (in case of {@link IDeltaBag}) is the first negative occurrence of the value | ||
65 | * | ||
66 | * @since 2.3 | ||
67 | */ | ||
68 | boolean removeOneOrNop(T value); | ||
69 | |||
70 | /** | ||
71 | * Removes all occurrences of the given value from the memory. | ||
72 | */ | ||
73 | void clearAllOf(T value); | ||
74 | |||
75 | /** | ||
76 | * Empties out the memory. | ||
77 | */ | ||
78 | @Override | ||
79 | void clear(); | ||
80 | |||
81 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java new file mode 100644 index 00000000..add575c6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java | |||
@@ -0,0 +1,205 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Set; | ||
15 | import java.util.function.BiConsumer; | ||
16 | import java.util.stream.Stream; | ||
17 | import java.util.stream.StreamSupport; | ||
18 | |||
19 | /** | ||
20 | * A read-only view on a memory containing a positive or negative number of equal() copies for some values. | ||
21 | * During iterations, each distinct value is iterated only once. | ||
22 | * | ||
23 | * <p> See {@link IMemory}. | ||
24 | * | ||
25 | * <p> Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMemoryView)} and {@link #equals(IMemoryView, Object)} here. | ||
26 | * | ||
27 | * @author Gabor Bergmann | ||
28 | * | ||
29 | * @since 2.0 | ||
30 | */ | ||
31 | public interface IMemoryView<T> extends Iterable<T> { | ||
32 | |||
33 | /** | ||
34 | * Returns the number of occurrences of the given value. | ||
35 | * | ||
36 | * @return the number of occurrences | ||
37 | */ | ||
38 | int getCount(T value); | ||
39 | |||
40 | /** | ||
41 | * Returns the number of occurrences of the given value (which may be of any type). | ||
42 | * | ||
43 | * @return the number of occurrences | ||
44 | */ | ||
45 | int getCountUnsafe(Object value); | ||
46 | |||
47 | /** | ||
48 | * @return true if the given value is contained with a nonzero multiplicity | ||
49 | */ | ||
50 | boolean containsNonZero(T value); | ||
51 | |||
52 | /** | ||
53 | * @return true if the given value (which may be of any type) is contained with a nonzero multiplicity | ||
54 | */ | ||
55 | boolean containsNonZeroUnsafe(Object value); | ||
56 | |||
57 | /** | ||
58 | * @return the number of distinct values | ||
59 | */ | ||
60 | int size(); | ||
61 | |||
62 | /** | ||
63 | * | ||
64 | * @return iff contains at least one value with non-zero occurrences | ||
65 | */ | ||
66 | boolean isEmpty(); | ||
67 | |||
68 | /** | ||
69 | * The set of distinct values | ||
70 | */ | ||
71 | Set<T> distinctValues(); | ||
72 | |||
73 | |||
74 | /** | ||
75 | * Where supported, returns the stored element that is equal to the given value, or null if none. | ||
76 | * Useful for canonicalization in case of non-identity equals(). | ||
77 | * | ||
78 | * <p> For collections that do not support canonicalization, simply returns the argument if contained, null if none. | ||
79 | * | ||
80 | * @return a value equal to the argument if such a value is stored, or null if none | ||
81 | */ | ||
82 | default T theContainedVersionOf(T value) { | ||
83 | if (containsNonZero(value)) return value; else return null; | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Where supported, returns the stored element that is equal to the given value (of any type), | ||
88 | * or null if none. | ||
89 | * Useful for canonicalization in case of non-identity equals(). | ||
90 | * | ||
91 | * <p> For collections that do not support canonicalization, simply returns the argument if contained, null if none. | ||
92 | * | ||
93 | * @return a value equal to the argument if such a value is stored, or null if none | ||
94 | */ | ||
95 | @SuppressWarnings("unchecked") | ||
96 | default T theContainedVersionOfUnsafe(Object value) { | ||
97 | if (containsNonZeroUnsafe(value)) return (T) value; else return null; | ||
98 | } | ||
99 | |||
100 | |||
101 | /** | ||
102 | * @return an unmodifiable view of contained values with their multiplicities | ||
103 | */ | ||
104 | default Iterable<Map.Entry<T, Integer>> entriesWithMultiplicities() { | ||
105 | return () -> { | ||
106 | Iterator<T> wrapped = distinctValues().iterator(); | ||
107 | return new Iterator<Map.Entry<T, Integer>> () { | ||
108 | @Override | ||
109 | public boolean hasNext() { | ||
110 | return wrapped.hasNext(); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public Map.Entry<T, Integer> next() { | ||
115 | T key = wrapped.next(); | ||
116 | int count = getCount(key); | ||
117 | return new Map.Entry<T, Integer>(){ | ||
118 | @Override | ||
119 | public T getKey() { | ||
120 | return key; | ||
121 | } | ||
122 | |||
123 | @Override | ||
124 | public Integer getValue() { | ||
125 | return count; | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public Integer setValue(Integer value) { | ||
130 | throw new UnsupportedOperationException(); | ||
131 | } | ||
132 | |||
133 | @Override | ||
134 | public String toString() { | ||
135 | return String.format("%d of %s", count, key); | ||
136 | } | ||
137 | |||
138 | }; | ||
139 | } | ||
140 | |||
141 | }; | ||
142 | }; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * Process contained values with their multiplicities | ||
147 | */ | ||
148 | default void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
149 | for (T value : distinctValues()) { | ||
150 | entryConsumer.accept(value, getCount(value)); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | |||
155 | /** | ||
156 | * For compatibility with legacy code relying on element-to-integer maps. | ||
157 | * @return an unmodifiable view of contained values with their multiplicities | ||
158 | */ | ||
159 | public default Map<T, Integer> asMap() { | ||
160 | return new MemoryViewBackedMapView<>(this); | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * For compatibility with legacy code relying on element-to-integer maps. | ||
165 | * @return an unmodifiable view of contained values with their multiplicities | ||
166 | */ | ||
167 | public static <T> IMemoryView<T> fromMap(Map<T, Integer> wrapped) { | ||
168 | return new MapBackedMemoryView<>(wrapped); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * @return a stream of values, iterable once | ||
173 | * @since 2.1 | ||
174 | */ | ||
175 | public default Stream<T> asStream() { | ||
176 | return StreamSupport.stream(spliterator(), false); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Provides semantic equality comparison. | ||
181 | */ | ||
182 | public static <T> boolean equals(IMemoryView<T> self, Object obj) { | ||
183 | if (obj instanceof IMemoryView<?>) { | ||
184 | IMemoryView<?> other = (IMemoryView<?>) obj; | ||
185 | if (other.size() != self.size()) return false; | ||
186 | for (Entry<?, Integer> entry : other.entriesWithMultiplicities()) { | ||
187 | if ( !entry.getValue().equals(self.getCountUnsafe(entry.getKey()))) | ||
188 | return false; | ||
189 | } | ||
190 | return true; | ||
191 | } | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Provides semantic hashCode() comparison. | ||
197 | */ | ||
198 | public static <T> int hashCode(IMemoryView<T> memory) { | ||
199 | int hashCode = 0; | ||
200 | for (T value : memory.distinctValues()) { | ||
201 | hashCode += value.hashCode() ^ Integer.hashCode(memory.getCount(value)); | ||
202 | } | ||
203 | return hashCode; | ||
204 | } | ||
205 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java new file mode 100644 index 00000000..1ce1d2c9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java | |||
@@ -0,0 +1,216 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.stream.Stream; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
14 | |||
15 | /** | ||
16 | * A multi-map that associates sets / multisets / delta sets of values to each key. | ||
17 | * | ||
18 | * <p> Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMultiLookup)} and {@link #equals(IMultiLookup, Object)} here. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * @since 2.0 | ||
22 | * @noimplement This interface is not intended to be implemented by clients. | ||
23 | */ | ||
24 | public interface IMultiLookup<Key, Value> { | ||
25 | |||
26 | /** | ||
27 | * Returns true if this collection is empty, false otherwise. | ||
28 | * @since 2.2 | ||
29 | */ | ||
30 | boolean isEmpty(); | ||
31 | |||
32 | |||
33 | /** | ||
34 | * Returns true if there are any values associated with the given key. | ||
35 | * @param key a key for which associated values are sought | ||
36 | * @since 2.3 | ||
37 | */ | ||
38 | boolean lookupExists(Key key); | ||
39 | |||
40 | /** | ||
41 | * Returns a (read-only) bucket of values associated with the given key. | ||
42 | * Clients must not modify the returned bucket. | ||
43 | * @param key a key for which associated values are sought | ||
44 | * @return null if key not found, a bucket of values otherwise | ||
45 | */ | ||
46 | IMemoryView<Value> lookup(Key key); | ||
47 | |||
48 | /** | ||
49 | * Returns a (read-only) bucket of values associated with the given key. | ||
50 | * Clients must not modify the returned bucket. | ||
51 | * @param key a key for which associated values are sought | ||
52 | * @return a bucket of values, never null | ||
53 | */ | ||
54 | default IMemoryView<Value> lookupOrEmpty(Key key) { | ||
55 | IMemoryView<Value> bucket = lookup(key); | ||
56 | return bucket == null ? EmptyMemory.instance() : bucket; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Returns a (read-only) bucket of values associated with the given key, while simultaneously removing them. | ||
61 | * Clients must not modify the returned bucket. | ||
62 | * @param key a key for which associated values are sought | ||
63 | * @return a bucket of values, never null | ||
64 | * @since 2.3 | ||
65 | */ | ||
66 | IMemoryView<Value> lookupAndRemoveAll(Key key); | ||
67 | |||
68 | /** | ||
69 | * Returns a (read-only) bucket of values associated with the given key, which can be of any type. | ||
70 | * Clients must not modify the returned bucket. | ||
71 | * @param key a key for which associated values are sought (may or may not be of Key type) | ||
72 | * @return null if key not found, a bucket of values otherwise | ||
73 | */ | ||
74 | IMemoryView<Value> lookupUnsafe(Object key); | ||
75 | |||
76 | /** | ||
77 | * Returns a (read-only) bucket of values associated with the given key. | ||
78 | * Clients must not modify the returned bucket. | ||
79 | * @param key a key for which associated values are sought (may or may not be of Key type) | ||
80 | * @return a bucket of values, never null | ||
81 | */ | ||
82 | default IMemoryView<Value> lookupUnsafeOrEmpty(Object key) { | ||
83 | IMemoryView<Value> bucket = lookupUnsafe(key); | ||
84 | return bucket == null ? EmptyMemory.instance() : bucket; | ||
85 | } | ||
86 | |||
87 | |||
88 | |||
89 | /** | ||
90 | * @return the set of distinct keys that have values associated. | ||
91 | */ | ||
92 | Iterable<Key> distinctKeys(); | ||
93 | |||
94 | /** | ||
95 | * @return the set of distinct keys that have values associated. | ||
96 | * @since 2.3 | ||
97 | */ | ||
98 | Stream<Key> distinctKeysStream(); | ||
99 | |||
100 | /** | ||
101 | * @return the number of distinct keys that have values associated. | ||
102 | */ | ||
103 | int countKeys(); | ||
104 | |||
105 | /** | ||
106 | * Iterates once over each distinct value. | ||
107 | */ | ||
108 | Iterable<Value> distinctValues(); | ||
109 | |||
110 | /** | ||
111 | * Iterates once over each distinct value. | ||
112 | * @since 2.3 | ||
113 | */ | ||
114 | Stream<Value> distinctValuesStream(); | ||
115 | |||
116 | |||
117 | |||
118 | /** | ||
119 | * How significant was the change? * | ||
120 | * @author Gabor Bergmann | ||
121 | */ | ||
122 | public enum ChangeGranularity { | ||
123 | /** | ||
124 | * First key-value pair with given key inserted, or last pair with given key deleted. | ||
125 | * (In case of delta maps, also if last negative key-value pair with given key neutralized.) | ||
126 | */ | ||
127 | KEY, | ||
128 | /** | ||
129 | * First occurrence of given key-value pair inserted, or last occurrence of the pair deleted, while key still has values associated. | ||
130 | * (In case of delta maps, also if last negative occurrence of key-value pair neutralized.) | ||
131 | */ | ||
132 | VALUE, | ||
133 | /** | ||
134 | * Duplicate key-value pair inserted or deleted. | ||
135 | */ | ||
136 | DUPLICATE | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * Adds key-value pair to the lookup structure, or fails if not possible. | ||
141 | * <p> If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), | ||
142 | * the operation throws an {@link IllegalStateException}. | ||
143 | * @return the granularity of the change | ||
144 | * @throws IllegalStateException if addition would cause duplication that is not permitted | ||
145 | */ | ||
146 | public ChangeGranularity addPair(Key key, Value value); | ||
147 | /** | ||
148 | * Adds key-value pair to the lookup structure. | ||
149 | * <p> If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), | ||
150 | * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. | ||
151 | * @return the granularity of the change, or {@link ChangeGranularity#DUPLICATE} if addition would result in a duplicate and therefore ignored | ||
152 | * @since 2.3 | ||
153 | */ | ||
154 | public ChangeGranularity addPairOrNop(Key key, Value value); | ||
155 | /** | ||
156 | * Removes key-value pair from the lookup structure, or fails if not possible. | ||
157 | * <p> When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type | ||
158 | * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), an {@link IllegalStateException} is thrown. | ||
159 | * @return the granularity of the change | ||
160 | * @throws IllegalStateException if removing non-existing element that is not permitted | ||
161 | */ | ||
162 | public ChangeGranularity removePair(Key key, Value value); | ||
163 | /** | ||
164 | * Removes key-value pair from the lookup structure. | ||
165 | * <p> When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type | ||
166 | * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), | ||
167 | * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. | ||
168 | * @return the granularity of the change | ||
169 | * @throws IllegalStateException if removing non-existing element that is not permitted | ||
170 | * @since 2.3 | ||
171 | */ | ||
172 | public ChangeGranularity removePairOrNop(Key key, Value value); | ||
173 | |||
174 | /** | ||
175 | * Updates multiplicity of key-value pair by a positive amount. | ||
176 | * | ||
177 | * <p> PRE: count > 0 | ||
178 | * | ||
179 | * @return the granularity of the change | ||
180 | * @throws IllegalStateException if addition would cause duplication that is not permitted | ||
181 | */ | ||
182 | public ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count); | ||
183 | |||
184 | /** | ||
185 | * Empties out the lookup structure. | ||
186 | */ | ||
187 | public void clear(); | ||
188 | |||
189 | /** | ||
190 | * Provides semantic equality comparison. | ||
191 | */ | ||
192 | public static <Key, Value> boolean equals(IMultiLookup<Key, Value> self, Object obj) { | ||
193 | if (obj instanceof IMultiLookup<?, ?>) { | ||
194 | IMultiLookup<?, ?> other = (IMultiLookup<?, ?>) obj; | ||
195 | if (other.countKeys() != self.countKeys()) return false; | ||
196 | for (Object key : other.distinctKeys()) { | ||
197 | if (! other.lookupUnsafe(key).equals(self.lookupUnsafe(key))) | ||
198 | return false; | ||
199 | } | ||
200 | return true; | ||
201 | } | ||
202 | return false; | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Provides semantic hashCode() comparison. | ||
207 | */ | ||
208 | public static <Key, Value> int hashCode(IMultiLookup<Key, Value> memory) { | ||
209 | int hashCode = 0; | ||
210 | for (Key key : memory.distinctKeys()) { | ||
211 | hashCode += key.hashCode() ^ memory.lookup(key).hashCode(); | ||
212 | } | ||
213 | return hashCode; | ||
214 | } | ||
215 | |||
216 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java new file mode 100644 index 00000000..8b1944c1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java | |||
@@ -0,0 +1,485 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | import java.util.Objects; | ||
15 | import java.util.stream.Stream; | ||
16 | import java.util.stream.StreamSupport; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; | ||
19 | |||
20 | /** | ||
21 | * Specialized multimap implementation that saves memory | ||
22 | * by storing singleton value objects (multiplicity 1) instead of multiset buckets | ||
23 | * whenever there is only one value associated with a key. | ||
24 | * | ||
25 | * <p> See specialized {@link ToSetsAbstract}, {@link ToMultisetsAbstract} for various bucket types. | ||
26 | * | ||
27 | * <p> Implemented as a Key->Object map with invariant: <ul> | ||
28 | * <li> key maps to null if associated with no values; | ||
29 | * <li> key maps to a single Value iff it is associated with a single value of multiplicity +1; | ||
30 | * <li> key maps to Bucket otherwise | ||
31 | * </ul> | ||
32 | * | ||
33 | * Note that due to the above invariant, handling +1 and -1 are asymmetric in case of delta maps. | ||
34 | * | ||
35 | * <p> Not intended as an API, but rather as a 'base class' for implementors. | ||
36 | * Realized as an interface with default implementations, instead of an abstract class, | ||
37 | * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. | ||
38 | * | ||
39 | * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible) | ||
40 | * and bind the lowLevel* methods accordingly. | ||
41 | * | ||
42 | * @noreference This interface is not intended to be referenced by clients. | ||
43 | * @noimplement This interface is not intended to be implemented by clients. | ||
44 | * | ||
45 | * @author Gabor Bergmann | ||
46 | * @since 2.0 | ||
47 | * | ||
48 | * | ||
49 | */ | ||
50 | public interface IMultiLookupAbstract<Key, Value, Bucket extends MarkedMemory<Value>> extends IMultiLookup<Key, Value> { | ||
51 | |||
52 | // the following methods must be bound to a concrete Map<Key,Object>-like structure (primitive implementation allowed) | ||
53 | |||
54 | /** | ||
55 | * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map | ||
56 | */ | ||
57 | abstract Object lowLevelGet(Key key); | ||
58 | |||
59 | /** | ||
60 | * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map | ||
61 | */ | ||
62 | abstract Object lowLevelGetUnsafe(Object key); | ||
63 | |||
64 | /** | ||
65 | * Implementor shall bind to the low-level remove() or equivalent of the underlying Key-to-Object map | ||
66 | */ | ||
67 | abstract Object lowLevelRemove(Key key); | ||
68 | |||
69 | /** | ||
70 | * Implementor shall bind to the low-level putIfAbsent() or equivalent of the underlying Key-to-Object map | ||
71 | */ | ||
72 | abstract Object lowLevelPutIfAbsent(Key key, Value value); | ||
73 | |||
74 | /** | ||
75 | * Implementor shall bind to the low-level put() or equivalent of the underlying Key-to-Object map | ||
76 | */ | ||
77 | abstract void lowLevelPut(Key key, Object valueOrBucket); | ||
78 | |||
79 | /** | ||
80 | * Implementor shall bind to the low-level values() or equivalent of the underlying Key-to-Object map | ||
81 | */ | ||
82 | abstract Iterable<Object> lowLevelValues(); | ||
83 | |||
84 | /** | ||
85 | * Implementor shall bind to the low-level keySet() or equivalent of the underlying Key-to-Object map | ||
86 | */ | ||
87 | abstract Iterable<Key> lowLevelKeySet(); | ||
88 | |||
89 | /** | ||
90 | * Implementor shall bind to the low-level size() or equivalent of the underlying Key-to-Object map | ||
91 | */ | ||
92 | abstract int lowLevelSize(); | ||
93 | |||
94 | |||
95 | // generic multi-lookup logic | ||
96 | |||
97 | @Override | ||
98 | default boolean lookupExists(Key key) { | ||
99 | Object object = lowLevelGet(key); | ||
100 | return null != object; | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public default IMemoryView<Value> lookup(Key key) { | ||
105 | Object object = lowLevelGet(key); | ||
106 | if (object == null) return null; | ||
107 | if (object instanceof MarkedMemory) return (Bucket) object; | ||
108 | return yieldSingleton((Value)object); | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | default IMemoryView<Value> lookupAndRemoveAll(Key key) { | ||
113 | Object object = lowLevelRemove(key); | ||
114 | if (object == null) return EmptyMemory.instance(); | ||
115 | if (object instanceof MarkedMemory) return (Bucket) object; | ||
116 | return yieldSingleton((Value)object); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public default IMemoryView<Value> lookupUnsafe(Object key) { | ||
121 | Object object = lowLevelGetUnsafe(key); | ||
122 | if (object == null) return null; | ||
123 | if (object instanceof MarkedMemory) return (Bucket) object; | ||
124 | return yieldSingleton((Value)object); | ||
125 | } | ||
126 | |||
127 | @Override | ||
128 | public default ChangeGranularity addPair(Key key, Value value) { | ||
129 | return addPairInternal(key, value, true); | ||
130 | } | ||
131 | |||
132 | @Override | ||
133 | default ChangeGranularity addPairOrNop(Key key, Value value) { | ||
134 | return addPairInternal(key, value, false); | ||
135 | } | ||
136 | |||
137 | public default ChangeGranularity addPairInternal(Key key, Value value, boolean throwIfImpossible) { | ||
138 | Object old = lowLevelPutIfAbsent(key, value); | ||
139 | boolean keyChange = (old == null); | ||
140 | |||
141 | if (keyChange) { // key was not present | ||
142 | return ChangeGranularity.KEY; | ||
143 | } else { // key was already present | ||
144 | Bucket bucket; | ||
145 | if (old instanceof MarkedMemory) { // ... as collection | ||
146 | bucket = (Bucket) old; | ||
147 | } else { // ... as singleton | ||
148 | if (!this.duplicatesAllowed() && Objects.equals(value, old)) { | ||
149 | if (throwIfImpossible) | ||
150 | throw new IllegalStateException(); | ||
151 | else | ||
152 | return ChangeGranularity.DUPLICATE; | ||
153 | } | ||
154 | bucket = createSingletonBucket((Value) old); | ||
155 | lowLevelPut(key, bucket); | ||
156 | } | ||
157 | // will throw if forbidden duplicate, return false if allowed duplicate | ||
158 | if (addToBucket(bucket, value, throwIfImpossible)) { | ||
159 | // deltas may become empty or a singleton after addition! | ||
160 | if (negativesAllowed()) { | ||
161 | if (bucket.isEmpty()) { | ||
162 | lowLevelRemove(key); | ||
163 | return ChangeGranularity.KEY; | ||
164 | } else { | ||
165 | handleSingleton(key, bucket); | ||
166 | return ChangeGranularity.VALUE; | ||
167 | } | ||
168 | } else return ChangeGranularity.VALUE; | ||
169 | } else return ChangeGranularity.DUPLICATE; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | @Override | ||
174 | // TODO deltas not supproted yet | ||
175 | default ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count) { | ||
176 | if (count == 1) return addPair(key, value); | ||
177 | // count > 1, always end up with non-singleton bucket | ||
178 | |||
179 | Object old = lowLevelGet(key); | ||
180 | boolean keyChange = (old == null); | ||
181 | |||
182 | Bucket bucket; | ||
183 | if (keyChange) { // ... nothing associated to key yet | ||
184 | bucket = createSingletonBucket(value); | ||
185 | lowLevelPut(key, bucket); | ||
186 | --count; // one less to increment later | ||
187 | } else if (old instanceof MarkedMemory) { // ... as collection | ||
188 | bucket = (Bucket) old; | ||
189 | } else { // ... as singleton | ||
190 | bucket = createSingletonBucket((Value) old); | ||
191 | lowLevelPut(key, bucket); | ||
192 | } | ||
193 | |||
194 | boolean newValue = bucket.addSigned(value, count); | ||
195 | |||
196 | if (keyChange) return ChangeGranularity.KEY; | ||
197 | else if (newValue) return ChangeGranularity.VALUE; | ||
198 | else return ChangeGranularity.DUPLICATE; | ||
199 | } | ||
200 | |||
201 | @Override | ||
202 | public default ChangeGranularity removePair(Key key, Value value) { | ||
203 | return removePairInternal(key, value, true); | ||
204 | } | ||
205 | |||
206 | @Override | ||
207 | default ChangeGranularity removePairOrNop(Key key, Value value) { | ||
208 | return removePairInternal(key, value, false); | ||
209 | } | ||
210 | |||
211 | public default ChangeGranularity removePairInternal(Key key, Value value, boolean throwIfImpossible) { | ||
212 | Object old = lowLevelGet(key); | ||
213 | if (old instanceof MarkedMemory) { // ... as collection | ||
214 | @SuppressWarnings("unchecked") | ||
215 | Bucket bucket = (Bucket) old; | ||
216 | // will throw if removing non-existent, return false if removing duplicate | ||
217 | boolean valueChange = removeFromBucket(bucket, value, throwIfImpossible); | ||
218 | handleSingleton(key, bucket); | ||
219 | if (valueChange) | ||
220 | return ChangeGranularity.VALUE; | ||
221 | else | ||
222 | return ChangeGranularity.DUPLICATE; | ||
223 | } else if (value.equals(old)) { // matching singleton | ||
224 | lowLevelRemove(key); | ||
225 | return ChangeGranularity.KEY; | ||
226 | } else { // different singleton, will produce a delta if possible | ||
227 | if (negativesAllowed()) { | ||
228 | Bucket deltaBucket = createDeltaBucket((Value) old, value); // will throw if no deltas supported | ||
229 | lowLevelPut(key, deltaBucket); | ||
230 | return ChangeGranularity.VALUE; // no key change | ||
231 | } else { | ||
232 | if (throwIfImpossible) | ||
233 | throw new IllegalStateException(); | ||
234 | else | ||
235 | return ChangeGranularity.DUPLICATE; | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
240 | public default void handleSingleton(Key key, Bucket bucket) { | ||
241 | Value remainingSingleton = asSingleton(bucket); | ||
242 | if (remainingSingleton != null) { // only one remains | ||
243 | lowLevelPut(key, remainingSingleton); | ||
244 | } | ||
245 | } | ||
246 | |||
247 | @Override | ||
248 | public default Iterable<Value> distinctValues() { | ||
249 | return new Iterable<Value>() { | ||
250 | private final Iterator<Value> EMPTY_ITERATOR = Collections.<Value>emptySet().iterator(); | ||
251 | @Override | ||
252 | public Iterator<Value> iterator() { | ||
253 | return new Iterator<Value>() { | ||
254 | Iterator<Object> bucketIterator = lowLevelValues().iterator(); | ||
255 | Iterator<Value> elementIterator = EMPTY_ITERATOR; | ||
256 | |||
257 | @Override | ||
258 | public boolean hasNext() { | ||
259 | return (elementIterator.hasNext() || bucketIterator.hasNext()); | ||
260 | } | ||
261 | |||
262 | @Override | ||
263 | public Value next() { | ||
264 | if (elementIterator.hasNext()) | ||
265 | return elementIterator.next(); | ||
266 | else if (bucketIterator.hasNext()) { | ||
267 | Object bucket = bucketIterator.next(); | ||
268 | if (bucket instanceof MarkedMemory) { | ||
269 | elementIterator = | ||
270 | ((MarkedMemory) bucket).distinctValues().iterator(); | ||
271 | return elementIterator.next(); | ||
272 | } else { | ||
273 | elementIterator = EMPTY_ITERATOR; | ||
274 | return (Value) bucket; | ||
275 | } | ||
276 | } else | ||
277 | throw new NoSuchElementException(); | ||
278 | } | ||
279 | |||
280 | /** | ||
281 | * Not implemented | ||
282 | */ | ||
283 | @Override | ||
284 | public void remove() { | ||
285 | throw new UnsupportedOperationException(); | ||
286 | } | ||
287 | |||
288 | }; | ||
289 | } | ||
290 | }; | ||
291 | } | ||
292 | |||
293 | @Override | ||
294 | default Stream<Value> distinctValuesStream() { | ||
295 | return StreamSupport.stream(distinctValues().spliterator(), false); | ||
296 | } | ||
297 | |||
298 | @Override | ||
299 | default Iterable<Key> distinctKeys() { | ||
300 | return lowLevelKeySet(); | ||
301 | } | ||
302 | |||
303 | @Override | ||
304 | default Stream<Key> distinctKeysStream() { | ||
305 | return StreamSupport.stream(distinctKeys().spliterator(), false); | ||
306 | } | ||
307 | |||
308 | @Override | ||
309 | default int countKeys() { | ||
310 | return lowLevelSize(); | ||
311 | } | ||
312 | |||
313 | // the following methods are customized for bucket type | ||
314 | |||
315 | /** | ||
316 | * @return iff negative multiplicites are allowed | ||
317 | */ | ||
318 | abstract boolean negativesAllowed(); | ||
319 | |||
320 | /** | ||
321 | * @return iff larger-than-1 multiplicites are allowed | ||
322 | * @since 2.3 | ||
323 | */ | ||
324 | abstract boolean duplicatesAllowed(); | ||
325 | |||
326 | /** | ||
327 | * Increases the multiplicity of the value in the bucket. | ||
328 | * @return true iff non-duplicate | ||
329 | * @throws IllegalStateException if disallowed duplication and throwIfImpossible is specified | ||
330 | */ | ||
331 | abstract boolean addToBucket(Bucket bucket, Value value, boolean throwIfImpossible); | ||
332 | |||
333 | /** | ||
334 | * Decreases the multiplicity of the value in the bucket. | ||
335 | * @return false if removing duplicate value | ||
336 | * @throws IllegalStateException if removing non-existing value (unless delta map) and throwIfImpossible is specified | ||
337 | */ | ||
338 | abstract boolean removeFromBucket(Bucket bucket, Value value, boolean throwIfImpossible); | ||
339 | |||
340 | /** | ||
341 | * Checks whether the bucket is a singleton, i.e. it contains a single value with multiplicity +1 | ||
342 | * @return the singleton value, or null if the bucket is not singleton | ||
343 | */ | ||
344 | abstract Value asSingleton(Bucket bucket); | ||
345 | |||
346 | /** | ||
347 | * @return a new bucket consisting of a sole value | ||
348 | */ | ||
349 | abstract Bucket createSingletonBucket(Value value); | ||
350 | /** | ||
351 | * @return a read-only bucket consisting of a sole value, to be returned to the user | ||
352 | */ | ||
353 | default IMemoryView<Value> yieldSingleton(Value value) { | ||
354 | return new SingletonMemoryView<>(value); | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * @param positive the previously existing value, or null if the delta is to contain a single negative tuple | ||
359 | * @return a new bucket consisting of a delta of two values | ||
360 | * @throws IllegalStateException if deltas not supported | ||
361 | */ | ||
362 | abstract Bucket createDeltaBucket(Value positive, Value negative); | ||
363 | |||
364 | /** | ||
365 | * A multi-lookup whose buckets are sets. | ||
366 | * | ||
367 | * <p> Not intended as an API, but rather as a 'base class' for implementors. | ||
368 | * Realized as an interface with default implementations, instead of an abstract class, | ||
369 | * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. | ||
370 | * | ||
371 | * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible) | ||
372 | * and bind the lowLevel* methods accordingly. | ||
373 | * | ||
374 | * @noreference This interface is not intended to be referenced by clients. | ||
375 | * @noimplement This interface is not intended to be implemented by clients. | ||
376 | * @author Gabor Bergmann | ||
377 | */ | ||
378 | public static interface ToSetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedSet<Value>> { | ||
379 | /** | ||
380 | * @return a fresh, empty marked set | ||
381 | */ | ||
382 | public MarkedSet<Value> createMarkedSet(); | ||
383 | |||
384 | @Override | ||
385 | public default boolean negativesAllowed() { | ||
386 | return false; | ||
387 | } | ||
388 | @Override | ||
389 | default boolean duplicatesAllowed() { | ||
390 | return false; | ||
391 | } | ||
392 | |||
393 | @Override | ||
394 | public default boolean addToBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) { | ||
395 | if (bucket.addOne(value)) return true; | ||
396 | else if (throwIfImpossible) throw new IllegalStateException(); | ||
397 | else return false; | ||
398 | } | ||
399 | |||
400 | @Override | ||
401 | public default boolean removeFromBucket(MarkedSet<Value> bucket, Value value, boolean throwIfImpossible) { | ||
402 | return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); | ||
403 | } | ||
404 | |||
405 | @Override | ||
406 | public default Value asSingleton(MarkedSet<Value> bucket) { | ||
407 | return bucket.size() == 1 ? bucket.iterator().next() : null; | ||
408 | } | ||
409 | |||
410 | @Override | ||
411 | public default MarkedSet<Value> createSingletonBucket(Value value) { | ||
412 | MarkedSet<Value> result = createMarkedSet(); | ||
413 | result.addOne(value); | ||
414 | return result; | ||
415 | } | ||
416 | |||
417 | @Override | ||
418 | public default MarkedSet<Value> createDeltaBucket(Value positive, Value negative) { | ||
419 | throw new IllegalStateException(); | ||
420 | } | ||
421 | } | ||
422 | |||
423 | /** | ||
424 | * A multi-lookup whose buckets are multisets. | ||
425 | * | ||
426 | * <p> Not intended as an API, but rather as a 'base class' for implementors. | ||
427 | * Realized as an interface with default implementations, instead of an abstract class, | ||
428 | * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. | ||
429 | * | ||
430 | * <p> Implementor should inherit from a Map<Key, Object>-like class (primitive map possible) | ||
431 | * and bind the lowLevel* methods accordingly. | ||
432 | * | ||
433 | * @noreference This interface is not intended to be referenced by clients. | ||
434 | * @noimplement This interface is not intended to be implemented by clients. | ||
435 | * @author Gabor Bergmann | ||
436 | */ | ||
437 | public static interface ToMultisetsAbstract<Key, Value> extends IMultiLookupAbstract<Key, Value, MarkedMemory.MarkedMultiset<Value>> { | ||
438 | /** | ||
439 | * @return a fresh, empty marked multiset | ||
440 | */ | ||
441 | public MarkedMemory.MarkedMultiset<Value> createMarkedMultiset(); | ||
442 | |||
443 | @Override | ||
444 | public default boolean negativesAllowed() { | ||
445 | return false; | ||
446 | } | ||
447 | @Override | ||
448 | default boolean duplicatesAllowed() { | ||
449 | return true; | ||
450 | } | ||
451 | |||
452 | @Override | ||
453 | public default boolean addToBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) { | ||
454 | return bucket.addOne(value); | ||
455 | } | ||
456 | |||
457 | @Override | ||
458 | public default boolean removeFromBucket(MarkedMemory.MarkedMultiset<Value> bucket, Value value, boolean throwIfImpossible) { | ||
459 | return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); | ||
460 | } | ||
461 | |||
462 | @Override | ||
463 | public default Value asSingleton(MarkedMemory.MarkedMultiset<Value> bucket) { | ||
464 | if (bucket.size() != 1) return null; | ||
465 | Value candidate = bucket.iterator().next(); | ||
466 | return bucket.getCount(candidate) == 1 ? candidate : null; | ||
467 | } | ||
468 | |||
469 | @Override | ||
470 | public default MarkedMemory.MarkedMultiset<Value> createSingletonBucket(Value value) { | ||
471 | MarkedMemory.MarkedMultiset<Value> result = createMarkedMultiset(); | ||
472 | result.addOne(value); | ||
473 | return result; | ||
474 | } | ||
475 | |||
476 | @Override | ||
477 | public default MarkedMemory.MarkedMultiset<Value> createDeltaBucket(Value positive, Value negative) { | ||
478 | throw new IllegalStateException(); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | |||
483 | // TODO add ToDeltaBagsAbstract | ||
484 | |||
485 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java new file mode 100644 index 00000000..bdd5d597 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java | |||
@@ -0,0 +1,30 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * An {@link IMemory} that always contains values with a nonnegative multiplicity. | ||
13 | * | ||
14 | * <p> In case a write operation caused underflow, an {@link IllegalStateException} is thrown. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * @since 1.7 | ||
18 | */ | ||
19 | public interface IMultiset<T> extends IMemory<T> { | ||
20 | |||
21 | /** | ||
22 | * Adds the given number of occurrences to the memory. The count value must be a positive number. | ||
23 | * | ||
24 | * @param count | ||
25 | * the number of occurrences | ||
26 | * @return true if the tuple was not present before in the memory | ||
27 | */ | ||
28 | boolean addPositive(T value, int count); | ||
29 | |||
30 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java new file mode 100644 index 00000000..cd25dc95 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java | |||
@@ -0,0 +1,30 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, 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.util; | ||
10 | |||
11 | import java.util.function.Function; | ||
12 | import java.util.function.Supplier; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | |||
16 | /** | ||
17 | * A provider interface useful in various registry instances. | ||
18 | * | ||
19 | * @author Zoltan Ujhelyi | ||
20 | * | ||
21 | */ | ||
22 | public interface IProvider<T> extends Supplier<T>{ | ||
23 | |||
24 | public final class ProvidedValueFunction implements Function<IProvider<PQuery>, PQuery> { | ||
25 | @Override | ||
26 | public PQuery apply(IProvider<PQuery> input) { | ||
27 | return (input == null) ? null : input.get(); | ||
28 | } | ||
29 | } | ||
30 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java new file mode 100644 index 00000000..0c03da48 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.function.BiConsumer; | ||
12 | |||
13 | /** | ||
14 | * An {@link IMemory} that always contains values with a 0 or +1 multiplicity. | ||
15 | * | ||
16 | * <p> In case a write operation causes underflow or overflow, an {@link IllegalStateException} is thrown. | ||
17 | * | ||
18 | * @author Gabor Bergmann | ||
19 | * @since 2.0 | ||
20 | */ | ||
21 | public interface ISetMemory<T> extends IMemory<T> { | ||
22 | |||
23 | @Override | ||
24 | default void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
25 | for (T t : this.distinctValues()) entryConsumer.accept(t, 1); | ||
26 | } | ||
27 | |||
28 | |||
29 | @Override | ||
30 | default boolean removeOne(T value) { | ||
31 | if (!removeOneOrNop(value)) | ||
32 | throw new IllegalStateException(); | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | |||
37 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java new file mode 100644 index 00000000..3be078bd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java | |||
@@ -0,0 +1,102 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Set; | ||
15 | import java.util.function.BiConsumer; | ||
16 | |||
17 | /** | ||
18 | * Wraps a Map<T, Integer> (mapping elements to non-zero multiplicities) into an {@link IMemoryView}. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * @since 2.0 | ||
22 | */ | ||
23 | public class MapBackedMemoryView<T> implements IMemoryView<T> { | ||
24 | |||
25 | private Map<T, Integer> wrapped; | ||
26 | |||
27 | /** | ||
28 | * @param wrapped an equivalent map from contained objects to multiplicities | ||
29 | */ | ||
30 | protected MapBackedMemoryView(Map<T, Integer> wrapped) { | ||
31 | super(); | ||
32 | this.wrapped = wrapped; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Iterator<T> iterator() { | ||
37 | return wrapped.keySet().iterator(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public int getCount(T value) { | ||
42 | return getCountUnsafe(value); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int getCountUnsafe(Object value) { | ||
47 | Integer count = wrapped.get(value); | ||
48 | return count == null ? 0 : count; | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public boolean containsNonZero(T value) { | ||
53 | return wrapped.containsKey(value); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public boolean containsNonZeroUnsafe(Object value) { | ||
58 | return wrapped.containsKey(value); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public int size() { | ||
63 | return wrapped.size(); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public boolean isEmpty() { | ||
68 | return wrapped.isEmpty(); | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Set<T> distinctValues() { | ||
73 | return wrapped.keySet(); | ||
74 | } | ||
75 | |||
76 | |||
77 | @Override | ||
78 | public void forEachEntryWithMultiplicities(BiConsumer<T, Integer> entryConsumer) { | ||
79 | for (Entry<T, Integer> entry : wrapped.entrySet()) { | ||
80 | entryConsumer.accept(entry.getKey(), entry.getValue()); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public Iterable<Entry<T, Integer>> entriesWithMultiplicities() { | ||
86 | return wrapped.entrySet(); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public int hashCode() { | ||
91 | return IMemoryView.hashCode(this); | ||
92 | } | ||
93 | @Override | ||
94 | public boolean equals(Object obj) { | ||
95 | return IMemoryView.equals(this, obj); | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public String toString() { | ||
100 | return wrapped.toString(); | ||
101 | } | ||
102 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java new file mode 100644 index 00000000..d22dcbe7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java | |||
@@ -0,0 +1,21 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. | ||
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.util; | ||
10 | |||
11 | /** | ||
12 | * Internal marker type, must only be instantiated inside implementors of IMultiLookupImpl | ||
13 | * @noimplement This interface is not intended to be implemented by clients. | ||
14 | * @since 2.0 | ||
15 | */ | ||
16 | public interface MarkedMemory<Value> extends IMemory<Value> { | ||
17 | |||
18 | static interface MarkedSet<Value> extends MarkedMemory<Value> {} | ||
19 | static interface MarkedMultiset<Value> extends MarkedMemory<Value> {} | ||
20 | static interface MarkedDeltaBag<Value> extends MarkedMemory<Value> {} | ||
21 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java new file mode 100644 index 00000000..49711a89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java | |||
@@ -0,0 +1,117 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.Map; | ||
15 | import java.util.Set; | ||
16 | |||
17 | /** | ||
18 | * A partial and read-only Map implementation, mapping elements to multiplicities backed by an {@link IMemoryView}. | ||
19 | * | ||
20 | * <p> Not implemented: write methods. | ||
21 | * | ||
22 | * <p> Inefficiently implemented: {@link #containsValue(Object)}, {@link #values()}, {@link #entrySet()}. | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | * @since 2.0 | ||
26 | */ | ||
27 | public class MemoryViewBackedMapView<T> implements Map<T, Integer> { | ||
28 | |||
29 | private static final String READ_ONLY = "Read only"; | ||
30 | private final IMemoryView<T> wrapped; | ||
31 | |||
32 | /** | ||
33 | * @param wrapped a memory view whose contents are to be exposed as an element-to-integer map. | ||
34 | */ | ||
35 | protected MemoryViewBackedMapView(IMemoryView<T> wrapped) { | ||
36 | super(); | ||
37 | this.wrapped = wrapped; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public int size() { | ||
42 | return wrapped.size(); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public boolean isEmpty() { | ||
47 | return wrapped.isEmpty(); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public boolean containsKey(Object key) { | ||
52 | return wrapped.containsNonZeroUnsafe(key); | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public boolean containsValue(Object value) { | ||
57 | if (value instanceof Integer) { | ||
58 | for (Entry<T, Integer> entry : wrapped.entriesWithMultiplicities()) { | ||
59 | if (entry.getValue().equals(value)) return true; | ||
60 | } | ||
61 | } | ||
62 | return false; | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public Integer put(T key, Integer value) { | ||
67 | throw new UnsupportedOperationException(READ_ONLY); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public Integer get(Object key) { | ||
72 | int count = wrapped.getCountUnsafe(key); | ||
73 | if (count == 0) return null; else return count; | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public Integer remove(Object key) { | ||
78 | throw new UnsupportedOperationException(READ_ONLY); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public void putAll(Map<? extends T, ? extends Integer> m) { | ||
83 | throw new UnsupportedOperationException(READ_ONLY); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void clear() { | ||
88 | throw new UnsupportedOperationException(READ_ONLY); | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public Set<T> keySet() { | ||
93 | return wrapped.distinctValues(); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Collection<Integer> values() { | ||
98 | Collection<Integer> result = new ArrayList<>(); | ||
99 | wrapped.forEachEntryWithMultiplicities((value, count) -> result.add(count)); | ||
100 | return result; | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Set<Entry<T, Integer>> entrySet() { | ||
105 | Set<Entry<T, Integer>> result = new HashSet<>(); | ||
106 | for (Entry<T, Integer> entry : wrapped.entriesWithMultiplicities()) { | ||
107 | result.add(entry); | ||
108 | } | ||
109 | return result; | ||
110 | } | ||
111 | |||
112 | |||
113 | @Override | ||
114 | public String toString() { | ||
115 | return wrapped.toString(); | ||
116 | } | ||
117 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java new file mode 100644 index 00000000..e9e5e3a0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java | |||
@@ -0,0 +1,208 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.function.Supplier; | ||
12 | |||
13 | /** | ||
14 | * This class was motivated by the similar Preconditions class from Guava to provide simple precondition checking | ||
15 | * functionality. However, as starting with version 2.0 the runtime of VIATRA Query should not depend on Guava, the | ||
16 | * relevant functionality of the Preconditions checking functionality will be implemented here. | ||
17 | * | ||
18 | * @author Zoltan Ujhelyi | ||
19 | * @since 2.0 | ||
20 | * | ||
21 | */ | ||
22 | public final class Preconditions { | ||
23 | |||
24 | private Preconditions() { | ||
25 | /* Utility class constructor */ } | ||
26 | |||
27 | /** | ||
28 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
29 | * | ||
30 | * @param expression | ||
31 | * a boolean expression | ||
32 | * @throws IllegalArgumentException | ||
33 | * if {@code expression} is false | ||
34 | */ | ||
35 | public static void checkArgument(boolean expression) { | ||
36 | if (!expression) { | ||
37 | throw new IllegalArgumentException(); | ||
38 | } | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
43 | * | ||
44 | * @param expression | ||
45 | * a boolean expression | ||
46 | * @param errorMessage | ||
47 | * the exception message to use if the check fails | ||
48 | * @throws IllegalArgumentException | ||
49 | * if {@code expression} is false | ||
50 | */ | ||
51 | public static void checkArgument(boolean expression, String errorMessage) { | ||
52 | if (!expression) { | ||
53 | throw new IllegalArgumentException(errorMessage); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
59 | * | ||
60 | * @param expression | ||
61 | * a boolean expression | ||
62 | * @param errorMessageTemplate | ||
63 | * a template for the exception message should the check fail using the Java Formatter syntax; the same | ||
64 | * as used by {@link String#format(String, Object...)}. | ||
65 | * @param errorMessageArgs | ||
66 | * the arguments to be substituted into the message template. | ||
67 | * @throws IllegalArgumentException | ||
68 | * if {@code expression} is false | ||
69 | * @throws NullPointerException | ||
70 | * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't | ||
71 | * let this happen) | ||
72 | */ | ||
73 | public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { | ||
74 | if (!expression) { | ||
75 | throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * Ensures the truth of an expression involving one or more parameters to the calling method. | ||
81 | * | ||
82 | * @param expression | ||
83 | * a boolean expression | ||
84 | * @param messageSupplier a supplier that is called to calculate the error message if necessary | ||
85 | * @throws IllegalArgumentException | ||
86 | * if {@code expression} is false | ||
87 | */ | ||
88 | public static void checkArgument(boolean expression, Supplier<String> messageSupplier) { | ||
89 | if (!expression) { | ||
90 | throw new IllegalArgumentException(messageSupplier.get()); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Ensures the truth of an expression involving one or more fields of a class. | ||
96 | * | ||
97 | * @param expression | ||
98 | * a boolean expression | ||
99 | * @throws IllegalStateException | ||
100 | * if {@code expression} is false | ||
101 | */ | ||
102 | public static void checkState(boolean expression) { | ||
103 | if (!expression) { | ||
104 | throw new IllegalStateException(); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Ensures the truth of an expression involving one or more fields of a class. | ||
110 | * | ||
111 | * @param expression | ||
112 | * a boolean expression | ||
113 | * @param errorMessage | ||
114 | * the exception message to use if the check fails | ||
115 | * @throws IllegalStateException | ||
116 | * if {@code expression} is false | ||
117 | */ | ||
118 | public static void checkState(boolean expression, String errorMessage) { | ||
119 | if (!expression) { | ||
120 | throw new IllegalStateException(errorMessage); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Ensures the truth of an expression involving one or more fields of a class. | ||
126 | * | ||
127 | * @param expression | ||
128 | * a boolean expression | ||
129 | * @param errorMessageTemplate | ||
130 | * a template for the exception message should the check fail using the Java Formatter syntax; the same | ||
131 | * as used by {@link String#format(String, Object...)}. | ||
132 | * @param errorMessageArgs | ||
133 | * the arguments to be substituted into the message template. | ||
134 | * @throws IllegalStateException | ||
135 | * if {@code expression} is false | ||
136 | * @throws NullPointerException | ||
137 | * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't | ||
138 | * let this happen) | ||
139 | */ | ||
140 | public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { | ||
141 | if (!expression) { | ||
142 | throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * Ensures the truth of an expression involving one or more fields of a class. | ||
148 | * | ||
149 | * @param expression | ||
150 | * a boolean expression | ||
151 | * @param messageSupplier a supplier that is called to calculate the error message if necessary | ||
152 | * @throws IllegalStateException | ||
153 | * if {@code expression} is false | ||
154 | */ | ||
155 | public static void checkState(boolean expression, Supplier<String> messageSupplier) { | ||
156 | if (!expression) { | ||
157 | throw new IllegalStateException(messageSupplier.get()); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * Ensures that an index is appropriate for a list or array of given size. | ||
163 | * | ||
164 | * @param index | ||
165 | * @param size | ||
166 | * @throws IndexOutOfBoundsException | ||
167 | * if index is negative or is greater or equal to size | ||
168 | */ | ||
169 | public static void checkElementIndex(int index, int size) { | ||
170 | if (index < 0 || index >= size) { | ||
171 | throw new IndexOutOfBoundsException(); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * Ensures that an index is appropriate for a list or array of given size. | ||
177 | * | ||
178 | * @param index | ||
179 | * @param size | ||
180 | * @param errorMessageTemplate | ||
181 | * a template for the exception message should the check fail using the Java Formatter syntax; the same | ||
182 | * as used by {@link String#format(String, Object...)}. | ||
183 | * @param errorMessageArgs | ||
184 | * the arguments to be substituted into the message template. | ||
185 | * @throws IndexOutOfBoundsException | ||
186 | * if index is negative or is greater or equal to size | ||
187 | */ | ||
188 | public static void checkElementIndex(int index, int size, String errorMessageTemplate, Object... errorMessageArgs) { | ||
189 | if (index < 0 || index >= size) { | ||
190 | throw new IndexOutOfBoundsException(String.format(errorMessageTemplate, errorMessageArgs)); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Ensures that an index is appropriate for a list or array of given size. | ||
196 | * | ||
197 | * @param index | ||
198 | * @param size | ||
199 | * @param messageSupplier a supplier that is called to calculate the error message if necessary | ||
200 | * @throws IndexOutOfBoundsException | ||
201 | * if index is negative or is greater or equal to size | ||
202 | */ | ||
203 | public static void checkElementIndex(int index, int size, Supplier<String> messageSupplier) { | ||
204 | if (index < 0 || index >= size) { | ||
205 | throw new IndexOutOfBoundsException(messageSupplier.get()); | ||
206 | } | ||
207 | } | ||
208 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java new file mode 100644 index 00000000..c4e6b5af --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.Map; | ||
13 | import java.util.function.Supplier; | ||
14 | |||
15 | /** | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | * @noreference This class is not intended to be referenced by clients. | ||
19 | */ | ||
20 | public class PurgableCache implements ICache { | ||
21 | |||
22 | Map<Object, Object> storage = new HashMap<>(); | ||
23 | |||
24 | @Override | ||
25 | @SuppressWarnings("unchecked") | ||
26 | public <T> T getValue(Object key, Class<? extends T> clazz, Supplier<T> valueProvider) { | ||
27 | if (storage.containsKey(key)) { | ||
28 | Object value = storage.get(key); | ||
29 | Preconditions.checkState(clazz.isInstance(value), "Cache stores for key %s a value of %s that is incompatible with the requested type %s", key, value, clazz); | ||
30 | return (T) value; | ||
31 | } else { | ||
32 | T value = valueProvider.get(); | ||
33 | storage.put(key, value); | ||
34 | return value; | ||
35 | } | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * Removes all values stored in the cache | ||
40 | */ | ||
41 | public void purge() { | ||
42 | storage.clear(); | ||
43 | } | ||
44 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java new file mode 100644 index 00000000..3749fe06 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java | |||
@@ -0,0 +1,90 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Gabor Bergmann, IncQuery Labs Ltd. | ||
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 | * Contributors: | ||
10 | * Gabor Bergmann - initial API and implementation | ||
11 | *******************************************************************************/ | ||
12 | package tools.refinery.viatra.runtime.matchers.util; | ||
13 | |||
14 | import java.util.ArrayList; | ||
15 | import java.util.List; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | import java.util.stream.Stream; | ||
19 | |||
20 | /** | ||
21 | * This class was motivated by the similar Sets class from Guava to provide simple set manipulation | ||
22 | * functionality. However, as starting with version 2.3 the runtime of VIATRA Query should not depend on Guava, | ||
23 | * not even internally, the relevant subset of Sets methods will be reimplemented here. | ||
24 | * | ||
25 | * <p> The current approach is to delegate to Eclipse Collections wherever possible. | ||
26 | * Such glue methods are useful so that downstream clients can avoid directly depending on Eclipse Collections. | ||
27 | * | ||
28 | * <p> Without an equivalent from Eclipse Collections, {@link #cartesianProduct(List)} is implemented here from scratch. | ||
29 | * | ||
30 | * @author Gabor Bergmann | ||
31 | * @since 2.3 | ||
32 | */ | ||
33 | public final class Sets { | ||
34 | |||
35 | /** | ||
36 | * @since 2.4 | ||
37 | */ | ||
38 | public static <A> Set<A> newSet(Iterable<A> elements) { | ||
39 | return org.eclipse.collections.impl.factory.Sets.mutable.ofAll(elements); | ||
40 | } | ||
41 | |||
42 | public static <A> Set<A> intersection(Set<A> left, Set<A> right) { | ||
43 | return org.eclipse.collections.impl.factory.Sets.intersect(left, right); | ||
44 | } | ||
45 | |||
46 | public static <A> Set<A> difference(Set<A> left, Set<A> right) { | ||
47 | return org.eclipse.collections.impl.factory.Sets.difference(left, right); | ||
48 | } | ||
49 | |||
50 | public static <A> Set<A> union(Set<A> left, Set<A> right) { | ||
51 | return org.eclipse.collections.impl.factory.Sets.union(left, right); | ||
52 | } | ||
53 | |||
54 | public static <A> Set<? extends Set<A>> powerSet(Set<A> set) { | ||
55 | return org.eclipse.collections.impl.factory.Sets.powerSet(set); | ||
56 | } | ||
57 | |||
58 | public static <A> Set<List<A>> cartesianProduct(List<? extends Set<? extends A>> setsList) { | ||
59 | |||
60 | class Suffix { // simple immutable linked list | ||
61 | private A head; | ||
62 | private Suffix next; | ||
63 | |||
64 | public Suffix(A head, Suffix next) { | ||
65 | super(); | ||
66 | this.head = head; | ||
67 | this.next = next; | ||
68 | } | ||
69 | |||
70 | public List<A> toList() { | ||
71 | ArrayList<A> result = new ArrayList<>(); | ||
72 | for (Suffix cursor = this; cursor!=null; cursor = cursor.next) | ||
73 | result.add(cursor.head); | ||
74 | return result; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // build result lists from end to start, in the form of suffixes | ||
79 | Stream<Suffix> suffixes = Stream.of((Suffix) null /* empty suffix*/); | ||
80 | for (int i = setsList.size()-1; i>=0; --i) { // iterate sets in reverse order | ||
81 | Set<? extends A> set = setsList.get(i); | ||
82 | suffixes = suffixes.flatMap(suffix -> set.stream().map(newElement -> new Suffix(newElement, suffix))); | ||
83 | } | ||
84 | |||
85 | |||
86 | return suffixes.map(Suffix::toList).collect(Collectors.toSet()); | ||
87 | } | ||
88 | |||
89 | |||
90 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java new file mode 100644 index 00000000..8f8bc228 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java | |||
@@ -0,0 +1,60 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | |||
13 | /** | ||
14 | * A piece of data associated with a direction. | ||
15 | * | ||
16 | * @author Tamas Szabo | ||
17 | * @since 2.4 | ||
18 | */ | ||
19 | public class Signed<Payload extends Comparable<Payload>> { | ||
20 | |||
21 | private final Payload payload; | ||
22 | private final Direction direction; | ||
23 | |||
24 | public Signed(final Direction direction, final Payload payload) { | ||
25 | this.payload = payload; | ||
26 | this.direction = direction; | ||
27 | } | ||
28 | |||
29 | public Payload getPayload() { | ||
30 | return payload; | ||
31 | } | ||
32 | |||
33 | public Direction getDirection() { | ||
34 | return direction; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public int hashCode() { | ||
39 | return Objects.hash(direction, payload); | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public boolean equals(final Object obj) { | ||
44 | if (this == obj) { | ||
45 | return true; | ||
46 | } else if (obj == null || this.getClass() != obj.getClass()) { | ||
47 | return false; | ||
48 | } else { | ||
49 | @SuppressWarnings("rawtypes") | ||
50 | final Signed other = (Signed) obj; | ||
51 | return direction == other.direction && Objects.equals(payload, other.payload); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public String toString() { | ||
57 | return this.direction.asSign() + this.payload.toString(); | ||
58 | } | ||
59 | |||
60 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java new file mode 100644 index 00000000..cc5963f7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, 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.util; | ||
10 | |||
11 | /** | ||
12 | * A provider implementation that always returns the same object instance. | ||
13 | * @author Zoltan Ujhelyi | ||
14 | */ | ||
15 | public class SingletonInstanceProvider<T> implements IProvider<T>{ | ||
16 | |||
17 | private T instance; | ||
18 | |||
19 | public SingletonInstanceProvider(T instance) { | ||
20 | Preconditions.checkArgument(instance != null, "Instance parameter must not be null."); | ||
21 | this.instance = instance; | ||
22 | } | ||
23 | |||
24 | @Override | ||
25 | public T get() { | ||
26 | return instance; | ||
27 | } | ||
28 | |||
29 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java new file mode 100644 index 00000000..b303f9ad --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java | |||
@@ -0,0 +1,105 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.NoSuchElementException; | ||
14 | import java.util.Set; | ||
15 | |||
16 | /** | ||
17 | * An immutable memory view that consists of a single non-null element with multiplicity 1. | ||
18 | * @author Gabor Bergmann | ||
19 | * @since 2.0 | ||
20 | */ | ||
21 | public final class SingletonMemoryView<Value> implements IMemoryView<Value> { | ||
22 | |||
23 | private Value wrapped; | ||
24 | private static final int ONE_HASH = Integer.valueOf(1).hashCode(); | ||
25 | |||
26 | public SingletonMemoryView(Value value) { | ||
27 | this.wrapped = value; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Iterator<Value> iterator() { | ||
32 | return new Iterator<Value>() { | ||
33 | boolean hasNext = true; | ||
34 | |||
35 | @Override | ||
36 | public boolean hasNext() { | ||
37 | return hasNext; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Value next() { | ||
42 | if (hasNext) { | ||
43 | hasNext = false; | ||
44 | return wrapped; | ||
45 | } else throw new NoSuchElementException(); | ||
46 | } | ||
47 | }; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public int getCount(Value value) { | ||
52 | return wrapped.equals(value) ? 1 : 0; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public int getCountUnsafe(Object value) { | ||
57 | return wrapped.equals(value) ? 1 : 0; | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public boolean containsNonZero(Value value) { | ||
62 | return wrapped.equals(value); | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public boolean containsNonZeroUnsafe(Object value) { | ||
67 | return wrapped.equals(value); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public int size() { | ||
72 | return 1; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public boolean isEmpty() { | ||
77 | return false; | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public Set<Value> distinctValues() { | ||
82 | return Collections.singleton(wrapped); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public boolean equals(Object obj) { | ||
87 | if (obj instanceof IMemoryView<?>) { | ||
88 | IMemoryView<?> other = (IMemoryView<?>) obj; | ||
89 | if (1 != other.size()) return false; | ||
90 | if (1 != other.getCountUnsafe(wrapped)) return false; | ||
91 | return true; | ||
92 | } | ||
93 | return false; | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public int hashCode() { | ||
98 | return wrapped.hashCode() ^ ONE_HASH; | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public String toString() { | ||
103 | return "{" + wrapped + "}"; | ||
104 | } | ||
105 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java new file mode 100644 index 00000000..90fcad4d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java | |||
@@ -0,0 +1,517 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.NavigableMap; | ||
15 | import java.util.Set; | ||
16 | import java.util.TreeMap; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.tuple.ITuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.resumable.Resumable; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.resumable.UnmaskedResumable; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; | ||
25 | |||
26 | /** | ||
27 | * A timely memory implementation that incrementally maintains the {@link Timeline}s of tuples. The memory is capable of | ||
28 | * lazy folding (see {@link Resumable}). | ||
29 | * | ||
30 | * @author Tamas Szabo | ||
31 | * @since 2.3 | ||
32 | */ | ||
33 | public class TimelyMemory<Timestamp extends Comparable<Timestamp>> implements Clearable, UnmaskedResumable<Timestamp> { | ||
34 | |||
35 | protected final Map<Tuple, TreeMap<Timestamp, CumulativeCounter>> counters; | ||
36 | protected final Map<Tuple, Timeline<Timestamp>> timelines; | ||
37 | public final TreeMap<Timestamp, Map<Tuple, FoldingState>> foldingState; | ||
38 | protected final Set<Tuple> presentAtInfinity; | ||
39 | protected final boolean isLazy; | ||
40 | protected final Diff<Timestamp> EMPTY_DIFF = new Diff<Timestamp>(); | ||
41 | |||
42 | public TimelyMemory() { | ||
43 | this(false); | ||
44 | } | ||
45 | |||
46 | public TimelyMemory(final boolean isLazy) { | ||
47 | this.counters = CollectionsFactory.createMap(); | ||
48 | this.timelines = CollectionsFactory.createMap(); | ||
49 | this.presentAtInfinity = CollectionsFactory.createSet(); | ||
50 | this.isLazy = isLazy; | ||
51 | if (isLazy) { | ||
52 | this.foldingState = CollectionsFactory.createTreeMap(); | ||
53 | } else { | ||
54 | this.foldingState = null; | ||
55 | } | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public Set<Tuple> getResumableTuples() { | ||
60 | if (this.foldingState == null || this.foldingState.isEmpty()) { | ||
61 | return Collections.emptySet(); | ||
62 | } else { | ||
63 | return this.foldingState.firstEntry().getValue().keySet(); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public Timestamp getResumableTimestamp() { | ||
69 | if (this.foldingState == null || this.foldingState.isEmpty()) { | ||
70 | return null; | ||
71 | } else { | ||
72 | return this.foldingState.firstKey(); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the | ||
78 | * two states will be merged together. | ||
79 | */ | ||
80 | protected void addFoldingState(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { | ||
81 | assert state.diff != 0; | ||
82 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.computeIfAbsent(timestamp, | ||
83 | k -> CollectionsFactory.createMap()); | ||
84 | tupleMap.compute(tuple, (k, v) -> { | ||
85 | return v == null ? state : v.merge(state); | ||
86 | }); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Map<Tuple, Diff<Timestamp>> resumeAt(final Timestamp timestamp) { | ||
91 | Timestamp current = this.getResumableTimestamp(); | ||
92 | if (current == null) { | ||
93 | throw new IllegalStateException("There is othing to fold!"); | ||
94 | } else if (current.compareTo(timestamp) != 0) { | ||
95 | // It can happen that already registered folding states end up having zero diffs, | ||
96 | // and we are instructed to continue folding at a timestamp that is higher | ||
97 | // than the lowest timestamp with a folding state. | ||
98 | // However, we only do garbage collection in doFoldingState, so now it is time to | ||
99 | // first clean up those states with zero diffs. | ||
100 | while (current != null && current.compareTo(timestamp) < 0) { | ||
101 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(current); | ||
102 | for (final Entry<Tuple, FoldingState> entry : tupleMap.entrySet()) { | ||
103 | final Tuple key = entry.getKey(); | ||
104 | final FoldingState value = entry.getValue(); | ||
105 | if (value.diff != 0) { | ||
106 | throw new IllegalStateException("Expected zero diff during garbage collection at " + current | ||
107 | + ", but the diff was " + value.diff + "!"); | ||
108 | } | ||
109 | doFoldingStep(key, value, current); | ||
110 | } | ||
111 | current = this.getResumableTimestamp(); | ||
112 | } | ||
113 | if (current == null || current.compareTo(timestamp) != 0) { | ||
114 | throw new IllegalStateException("Expected to continue folding at " + timestamp + "!"); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | final Map<Tuple, Diff<Timestamp>> diffMap = CollectionsFactory.createMap(); | ||
119 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(timestamp); | ||
120 | for (final Entry<Tuple, FoldingState> entry : tupleMap.entrySet()) { | ||
121 | final Tuple key = entry.getKey(); | ||
122 | final FoldingState value = entry.getValue(); | ||
123 | diffMap.put(key, doFoldingStep(key, value, timestamp)); | ||
124 | } | ||
125 | |||
126 | if (this.foldingState.get(timestamp) != null) { | ||
127 | throw new IllegalStateException( | ||
128 | "Folding at " + timestamp + " produced more folding work at the same timestamp!"); | ||
129 | } | ||
130 | |||
131 | return diffMap; | ||
132 | } | ||
133 | |||
134 | protected Diff<Timestamp> doFoldingStep(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { | ||
135 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
136 | if (state.diff == 0) { | ||
137 | gcCounters(counter, tuple, timestamp); | ||
138 | return EMPTY_DIFF; | ||
139 | } else { | ||
140 | final Diff<Timestamp> resultDiff = new Diff<>(); | ||
141 | final Timestamp nextTimestamp = this.counters.get(tuple).higherKey(timestamp); | ||
142 | |||
143 | final int oldCumulative = counter.cumulative; | ||
144 | |||
145 | counter.cumulative += state.diff; | ||
146 | |||
147 | computeDiffsLazy(state.diff < 0 ? Direction.DELETE : Direction.INSERT, oldCumulative, counter.cumulative, | ||
148 | timestamp, nextTimestamp, resultDiff); | ||
149 | |||
150 | gcCounters(counter, tuple, timestamp); | ||
151 | updateTimeline(tuple, resultDiff); | ||
152 | |||
153 | // prepare folding state for next timestamp | ||
154 | if (nextTimestamp != null) { | ||
155 | // propagate the incoming diff, not the diff stored in counter | ||
156 | addFoldingState(tuple, new FoldingState(state.diff), nextTimestamp); | ||
157 | } | ||
158 | |||
159 | return resultDiff; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * On-demand initializes and returns the counter for the given tuple and timestamp. | ||
165 | */ | ||
166 | protected CumulativeCounter getCounter(final Tuple tuple, final Timestamp timestamp) { | ||
167 | final TreeMap<Timestamp, CumulativeCounter> counterTimeline = this.counters.computeIfAbsent(tuple, | ||
168 | k -> CollectionsFactory.createTreeMap()); | ||
169 | |||
170 | final CumulativeCounter counter = counterTimeline.computeIfAbsent(timestamp, k -> { | ||
171 | final Entry<Timestamp, CumulativeCounter> previousCounter = counterTimeline.lowerEntry(k); | ||
172 | final int previousCumulative = previousCounter == null ? 0 : previousCounter.getValue().cumulative; | ||
173 | return new CumulativeCounter(0, previousCumulative); | ||
174 | }); | ||
175 | |||
176 | return counter; | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Garbage collects the counter of the given tuple and timestamp if the new diff is zero. | ||
181 | */ | ||
182 | protected void gcCounters(final CumulativeCounter counter, final Tuple tuple, final Timestamp timestamp) { | ||
183 | if (counter.diff == 0) { | ||
184 | final TreeMap<Timestamp, CumulativeCounter> counterMap = this.counters.get(tuple); | ||
185 | counterMap.remove(timestamp); | ||
186 | if (counterMap.isEmpty()) { | ||
187 | this.counters.remove(tuple); | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * Utility method that computes the timeline diffs in case of lazy memories. The diffs will be inserted into the | ||
194 | * input parameter. This method computes diffs for entire plateaus that spans from timestamp to nextTimestamp. | ||
195 | * | ||
196 | * Compared to the eager version of this method, the lazy version makes use of both the old and the new cumulative | ||
197 | * values because it can happen that the cumulative is incremented by a value that is larger than 1 (as folding | ||
198 | * states are merged together). This means that we cant decide whether the cumulative became positive by comparing | ||
199 | * the new value to 1. | ||
200 | */ | ||
201 | protected void computeDiffsLazy(final Direction direction, final int oldCumulative, final int newCumulative, | ||
202 | final Timestamp timestamp, final Timestamp nextTimestamp, final Diff<Timestamp> diffs) { | ||
203 | if (direction == Direction.INSERT) { | ||
204 | if (newCumulative == 0) { | ||
205 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
206 | } else { | ||
207 | if (oldCumulative == 0 /* current became positive */) { | ||
208 | // (1) either we sent out a DELETE before and now we need to cancel it, | ||
209 | // (2) or we just INSERT this for the first time | ||
210 | diffs.add(new Signed<>(Direction.INSERT, timestamp)); | ||
211 | if (nextTimestamp != null) { | ||
212 | diffs.add(new Signed<>(Direction.DELETE, nextTimestamp)); | ||
213 | } | ||
214 | } else /* current stays positive */ { | ||
215 | // nothing to do | ||
216 | } | ||
217 | } | ||
218 | } else { | ||
219 | if (newCumulative < 0) { | ||
220 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
221 | } else { | ||
222 | if (newCumulative == 0 /* current became zero */) { | ||
223 | diffs.add(new Signed<>(Direction.DELETE, timestamp)); | ||
224 | if (nextTimestamp != null) { | ||
225 | diffs.add(new Signed<>(Direction.INSERT, nextTimestamp)); | ||
226 | } | ||
227 | } else /* current stays positive */ { | ||
228 | // nothing to do | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * Utility method that computes the timeline diffs in case of eager memories. The diffs will be inserted into the | ||
236 | * input parameter. This method computes diffs that describe momentary changes instead of plateaus. Returns a | ||
237 | * {@link SignChange} that describes how the sign has changed at the given timestamp. | ||
238 | */ | ||
239 | protected SignChange computeDiffsEager(final Direction direction, final CumulativeCounter counter, | ||
240 | final SignChange signChangeAtPrevious, final Timestamp timestamp, final Diff<Timestamp> diffs) { | ||
241 | if (direction == Direction.INSERT) { | ||
242 | if (counter.cumulative == 0) { | ||
243 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
244 | } else { | ||
245 | if (counter.cumulative == 1 /* current became positive */) { | ||
246 | if (signChangeAtPrevious != SignChange.BECAME_POSITIVE) { | ||
247 | // (1) either we sent out a DELETE before and now we need to cancel it, | ||
248 | // (2) or we just INSERT this for the first time | ||
249 | diffs.add(new Signed<>(Direction.INSERT, timestamp)); | ||
250 | } else { | ||
251 | // we have already emitted this at the previous timestamp | ||
252 | // both previous and current became positive | ||
253 | throw new IllegalStateException( | ||
254 | "This would mean that the diff at current is 0 " + counter.diff); | ||
255 | } | ||
256 | |||
257 | // remember for next timestamp | ||
258 | return SignChange.BECAME_POSITIVE; | ||
259 | } else /* current stays positive */ { | ||
260 | if (signChangeAtPrevious == SignChange.BECAME_POSITIVE) { | ||
261 | // we sent out an INSERT before and now the timeline is positive already starting at previous | ||
262 | // we need to cancel the effect of this with a DELETE | ||
263 | diffs.add(new Signed<>(Direction.DELETE, timestamp)); | ||
264 | } else { | ||
265 | // this is normal, both previous and current was positive and stays positive | ||
266 | } | ||
267 | |||
268 | // remember for next timestamp | ||
269 | return SignChange.IRRELEVANT; | ||
270 | } | ||
271 | } | ||
272 | } else { | ||
273 | if (counter.cumulative < 0) { | ||
274 | throw new IllegalStateException("Cumulative count can never be negative!"); | ||
275 | } else { | ||
276 | if (counter.cumulative == 0 /* current became zero */) { | ||
277 | if (signChangeAtPrevious != SignChange.BECAME_ZERO) { | ||
278 | // (1) either we sent out a INSERT before and now we need to cancel it, | ||
279 | // (2) or we just DELETE this for the first time | ||
280 | diffs.add(new Signed<>(Direction.DELETE, timestamp)); | ||
281 | } else { | ||
282 | // we have already emitted this at the previous timestamp | ||
283 | // both previous and current became zero | ||
284 | throw new IllegalStateException( | ||
285 | "This would mean that the diff at current is 0 " + counter.diff); | ||
286 | } | ||
287 | |||
288 | // remember for next timestamp | ||
289 | return SignChange.BECAME_ZERO; | ||
290 | } else /* current stays positive */ { | ||
291 | if (signChangeAtPrevious == SignChange.BECAME_ZERO) { | ||
292 | // we sent out a DELETE before and now the timeline is zero already starting at previous | ||
293 | // we need to cancel the effect of this with a INSERT | ||
294 | diffs.add(new Signed<>(Direction.INSERT, timestamp)); | ||
295 | } else { | ||
296 | // this is normal, both previous and current was positive and stays positive | ||
297 | } | ||
298 | |||
299 | // remember for next timestamp | ||
300 | return SignChange.IRRELEVANT; | ||
301 | } | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | |||
306 | public Diff<Timestamp> put(final Tuple tuple, final Timestamp timestamp) { | ||
307 | if (this.isLazy) { | ||
308 | return putLazy(tuple, timestamp); | ||
309 | } else { | ||
310 | return putEager(tuple, timestamp); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | public Diff<Timestamp> remove(final Tuple tuple, final Timestamp timestamp) { | ||
315 | if (this.isLazy) { | ||
316 | return removeLazy(tuple, timestamp); | ||
317 | } else { | ||
318 | return removeEager(tuple, timestamp); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | protected Diff<Timestamp> putEager(final Tuple tuple, final Timestamp timestamp) { | ||
323 | final Diff<Timestamp> resultDiff = new Diff<>(); | ||
324 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
325 | ++counter.diff; | ||
326 | |||
327 | // before the INSERT timestamp, no change at all | ||
328 | // it cannot happen that those became positive in this round | ||
329 | SignChange signChangeAtPrevious = SignChange.IRRELEVANT; | ||
330 | |||
331 | final NavigableMap<Timestamp, CumulativeCounter> nextCounters = this.counters.get(tuple).tailMap(timestamp, | ||
332 | true); | ||
333 | for (final Entry<Timestamp, CumulativeCounter> currentEntry : nextCounters.entrySet()) { | ||
334 | final Timestamp currentTimestamp = currentEntry.getKey(); | ||
335 | final CumulativeCounter currentCounter = currentEntry.getValue(); | ||
336 | ++currentCounter.cumulative; | ||
337 | signChangeAtPrevious = computeDiffsEager(Direction.INSERT, currentCounter, signChangeAtPrevious, | ||
338 | currentTimestamp, resultDiff); | ||
339 | } | ||
340 | |||
341 | gcCounters(counter, tuple, timestamp); | ||
342 | updateTimeline(tuple, resultDiff); | ||
343 | |||
344 | return resultDiff; | ||
345 | } | ||
346 | |||
347 | protected Diff<Timestamp> putLazy(final Tuple tuple, final Timestamp timestamp) { | ||
348 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
349 | counter.diff += 1; | ||
350 | // before the INSERT timestamp, no change at all | ||
351 | // it cannot happen that those became positive in this round | ||
352 | addFoldingState(tuple, new FoldingState(+1), timestamp); | ||
353 | return EMPTY_DIFF; | ||
354 | } | ||
355 | |||
356 | protected Diff<Timestamp> removeEager(final Tuple tuple, final Timestamp timestamp) { | ||
357 | final Diff<Timestamp> resultDiff = new Diff<>(); | ||
358 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
359 | --counter.diff; | ||
360 | |||
361 | // before the DELETE timestamp, no change at all | ||
362 | // it cannot happen that those became zero in this round | ||
363 | SignChange signChangeAtPrevious = SignChange.IRRELEVANT; | ||
364 | |||
365 | final NavigableMap<Timestamp, CumulativeCounter> nextCounters = this.counters.get(tuple).tailMap(timestamp, | ||
366 | true); | ||
367 | for (final Entry<Timestamp, CumulativeCounter> currentEntry : nextCounters.entrySet()) { | ||
368 | final Timestamp currentTimestamp = currentEntry.getKey(); | ||
369 | final CumulativeCounter currentCounter = currentEntry.getValue(); | ||
370 | --currentCounter.cumulative; | ||
371 | signChangeAtPrevious = computeDiffsEager(Direction.DELETE, currentCounter, signChangeAtPrevious, | ||
372 | currentTimestamp, resultDiff); | ||
373 | } | ||
374 | |||
375 | gcCounters(counter, tuple, timestamp); | ||
376 | updateTimeline(tuple, resultDiff); | ||
377 | |||
378 | return resultDiff; | ||
379 | } | ||
380 | |||
381 | protected Diff<Timestamp> removeLazy(final Tuple tuple, final Timestamp timestamp) { | ||
382 | final CumulativeCounter counter = getCounter(tuple, timestamp); | ||
383 | counter.diff -= 1; | ||
384 | // before the DELETE timestamp, no change at all | ||
385 | // it cannot happen that those became zero in this round | ||
386 | addFoldingState(tuple, new FoldingState(-1), timestamp); | ||
387 | return EMPTY_DIFF; | ||
388 | } | ||
389 | |||
390 | /** | ||
391 | * Updates and garbage collects the timeline of the given tuple based on the given timeline diff. | ||
392 | */ | ||
393 | protected void updateTimeline(final Tuple tuple, final Diff<Timestamp> diff) { | ||
394 | if (!diff.isEmpty()) { | ||
395 | this.timelines.compute(tuple, (k, oldTimeline) -> { | ||
396 | this.presentAtInfinity.remove(tuple); | ||
397 | final Timeline<Timestamp> timeline = oldTimeline == null ? Timelines.createFrom(diff) | ||
398 | : oldTimeline.mergeAdditive(diff); | ||
399 | if (timeline.isPresentAtInfinity()) { | ||
400 | this.presentAtInfinity.add(tuple); | ||
401 | } | ||
402 | if (timeline.isEmpty()) { | ||
403 | return null; | ||
404 | } else { | ||
405 | return timeline; | ||
406 | } | ||
407 | }); | ||
408 | } | ||
409 | } | ||
410 | |||
411 | /** | ||
412 | * @since 2.8 | ||
413 | */ | ||
414 | public Set<Tuple> getTuplesAtInfinity() { | ||
415 | return this.presentAtInfinity; | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * Returns the number of tuples that are present at the moment 'infinity'. | ||
420 | */ | ||
421 | public int getCountAtInfinity() { | ||
422 | return this.presentAtInfinity.size(); | ||
423 | } | ||
424 | |||
425 | /** | ||
426 | * Returns true if the given tuple is present at the moment 'infinity'. | ||
427 | */ | ||
428 | public boolean isPresentAtInfinity(final Tuple tuple) { | ||
429 | final Timeline<Timestamp> timeline = this.timelines.get(tuple); | ||
430 | if (timeline == null) { | ||
431 | return false; | ||
432 | } else { | ||
433 | return timeline.isPresentAtInfinity(); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | public boolean isEmpty() { | ||
438 | return this.counters.isEmpty(); | ||
439 | } | ||
440 | |||
441 | public int size() { | ||
442 | return this.counters.size(); | ||
443 | } | ||
444 | |||
445 | public Set<Tuple> keySet() { | ||
446 | return this.counters.keySet(); | ||
447 | } | ||
448 | |||
449 | public Map<Tuple, Timeline<Timestamp>> asMap() { | ||
450 | return this.timelines; | ||
451 | } | ||
452 | |||
453 | public Timeline<Timestamp> get(final ITuple tuple) { | ||
454 | return this.timelines.get(tuple); | ||
455 | } | ||
456 | |||
457 | @Override | ||
458 | public void clear() { | ||
459 | this.counters.clear(); | ||
460 | this.timelines.clear(); | ||
461 | if (this.foldingState != null) { | ||
462 | this.foldingState.clear(); | ||
463 | } | ||
464 | } | ||
465 | |||
466 | public boolean containsKey(final ITuple tuple) { | ||
467 | return this.counters.containsKey(tuple); | ||
468 | } | ||
469 | |||
470 | @Override | ||
471 | public String toString() { | ||
472 | return this.counters + "\n" + this.timelines + "\n" + this.foldingState + "\n"; | ||
473 | } | ||
474 | |||
475 | protected static final class CumulativeCounter { | ||
476 | protected int diff; | ||
477 | protected int cumulative; | ||
478 | |||
479 | protected CumulativeCounter(final int diff, final int cumulative) { | ||
480 | this.diff = diff; | ||
481 | this.cumulative = cumulative; | ||
482 | } | ||
483 | |||
484 | @Override | ||
485 | public String toString() { | ||
486 | return "{diff=" + this.diff + ", cumulative=" + this.cumulative + "}"; | ||
487 | } | ||
488 | |||
489 | } | ||
490 | |||
491 | protected static final class FoldingState { | ||
492 | protected final int diff; | ||
493 | |||
494 | protected FoldingState(final int diff) { | ||
495 | this.diff = diff; | ||
496 | } | ||
497 | |||
498 | @Override | ||
499 | public String toString() { | ||
500 | return "{diff=" + this.diff + "}"; | ||
501 | } | ||
502 | |||
503 | /** | ||
504 | * The returned result will never be null, even if the resulting diff is zero. | ||
505 | */ | ||
506 | public FoldingState merge(final FoldingState that) { | ||
507 | Preconditions.checkArgument(that != null); | ||
508 | return new FoldingState(this.diff + that.diff); | ||
509 | } | ||
510 | |||
511 | } | ||
512 | |||
513 | protected enum SignChange { | ||
514 | BECAME_POSITIVE, BECAME_ZERO, IRRELEVANT; | ||
515 | } | ||
516 | |||
517 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java new file mode 100644 index 00000000..ea70e61d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, 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.util.resumable; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
15 | |||
16 | /** | ||
17 | * A masked {@link Resumable} implementation, which maintains lazy folding per tuple signature. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * @since 2.4 | ||
21 | */ | ||
22 | public interface MaskedResumable<Timestamp extends Comparable<Timestamp>> extends Resumable<Timestamp> { | ||
23 | |||
24 | /** | ||
25 | * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to | ||
26 | * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more | ||
27 | * folding to do towards higher timestamps. | ||
28 | */ | ||
29 | public Map<Tuple, Map<Tuple, Diff<Timestamp>>> resumeAt(final Timestamp timestamp); | ||
30 | |||
31 | /** | ||
32 | * Returns the set of signatures for which lazy folding shall be resumed at the next timestamp. | ||
33 | */ | ||
34 | public Iterable<Tuple> getResumableSignatures(); | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java new file mode 100644 index 00000000..2861df20 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, 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.util.resumable; | ||
10 | |||
11 | /** | ||
12 | * A resumable lazily folds its state towards higher timestamps. Folding shall be done in the increasing order of | ||
13 | * timestamps, and it shall be interrupted after each step. The resumable can then be instructed to resume the folding, | ||
14 | * one step at a time. | ||
15 | * | ||
16 | * @author Tamas Szabo | ||
17 | * @since 2.4 | ||
18 | */ | ||
19 | public interface Resumable<Timestamp extends Comparable<Timestamp>> { | ||
20 | |||
21 | /** | ||
22 | * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this | ||
23 | * resumable. | ||
24 | */ | ||
25 | public Timestamp getResumableTimestamp(); | ||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java new file mode 100644 index 00000000..1671940b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java | |||
@@ -0,0 +1,36 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Tamas Szabo, 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.util.resumable; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
15 | |||
16 | /** | ||
17 | * A unmasked {@link Resumable} implementation, which maintains lazy folding without caring about tuple signatures. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * @since 2.4 | ||
21 | */ | ||
22 | public interface UnmaskedResumable<Timestamp extends Comparable<Timestamp>> extends Resumable<Timestamp> { | ||
23 | |||
24 | /** | ||
25 | * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to | ||
26 | * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more | ||
27 | * folding to do towards higher timestamps. | ||
28 | */ | ||
29 | public Map<Tuple, Diff<Timestamp>> resumeAt(final Timestamp timestamp); | ||
30 | |||
31 | /** | ||
32 | * Returns the set of tuples for which lazy folding shall be resumed at the next timestamp. | ||
33 | */ | ||
34 | public Iterable<Tuple> getResumableTuples(); | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java new file mode 100644 index 00000000..0532d094 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java | |||
@@ -0,0 +1,111 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
17 | |||
18 | /** | ||
19 | * A compact timeline may cosist of an arbitrary amount of moments. | ||
20 | * It is backed by an {@link ArrayList}. | ||
21 | * | ||
22 | * @author Tamas Szabo | ||
23 | * @since 2.4 | ||
24 | */ | ||
25 | public class CompactTimeline<Timestamp extends Comparable<Timestamp>> extends Timeline<Timestamp> { | ||
26 | |||
27 | protected final List<Timestamp> elements; | ||
28 | |||
29 | CompactTimeline() { | ||
30 | this.elements = new ArrayList<>(); | ||
31 | } | ||
32 | |||
33 | CompactTimeline(final Timestamp timestamp) { | ||
34 | this(); | ||
35 | this.elements.add(timestamp); | ||
36 | } | ||
37 | |||
38 | CompactTimeline(final List<Timestamp> timestamps) { | ||
39 | this.elements = new ArrayList<>(timestamps.size()); | ||
40 | this.elements.addAll(timestamps); | ||
41 | } | ||
42 | |||
43 | CompactTimeline(final Diff<Timestamp> diff) { | ||
44 | this.elements = new ArrayList<>(diff.size()); | ||
45 | Direction expected = Direction.INSERT; | ||
46 | for (Signed<Timestamp> signed : diff) { | ||
47 | if (!expected.equals(signed.getDirection())) { | ||
48 | throw new IllegalStateException(String.format("Expected direction (%s) constraint violated! %s @%s", | ||
49 | expected, diff, signed.getPayload())); | ||
50 | } | ||
51 | this.elements.add(signed.getPayload()); | ||
52 | expected = expected.opposite(); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public Signed<Timestamp> getSigned(final int index) { | ||
58 | final Direction direction = index % 2 == 0 ? Direction.INSERT : Direction.DELETE; | ||
59 | return new Signed<>(direction, this.getUnsigned(index)); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Timestamp getUnsigned(final int index) { | ||
64 | if (this.elements.size() <= index) { | ||
65 | throw new IllegalArgumentException( | ||
66 | "Timeline size (" + this.size() + ") is smaller than requested index " + index + "!"); | ||
67 | } else { | ||
68 | return this.elements.get(index); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public int size() { | ||
74 | return this.elements.size(); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public boolean isPresentAtInfinity() { | ||
79 | // if it has an odd length, then it ends with "INSERT" | ||
80 | return this.size() % 2 == 1; | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public Iterable<Signed<Timestamp>> asChangeSequence() { | ||
85 | Iterable<Timestamp> outer = this.elements; | ||
86 | return () -> { | ||
87 | final Iterator<Timestamp> itr = outer.iterator(); | ||
88 | return new Iterator<Signed<Timestamp>>() { | ||
89 | Direction direction = Direction.INSERT; | ||
90 | |||
91 | @Override | ||
92 | public boolean hasNext() { | ||
93 | return itr.hasNext(); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Signed<Timestamp> next() { | ||
98 | final Signed<Timestamp> result = new Signed<Timestamp>(direction, itr.next()); | ||
99 | direction = direction.opposite(); | ||
100 | return result; | ||
101 | } | ||
102 | }; | ||
103 | }; | ||
104 | } | ||
105 | |||
106 | @Override | ||
107 | public boolean isEmpty() { | ||
108 | return this.elements.isEmpty(); | ||
109 | } | ||
110 | |||
111 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java new file mode 100644 index 00000000..cec6049e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
14 | |||
15 | /** | ||
16 | * The description of a delta that specifies how a {@link Timeline} changes. It consists of {@link Signed} timestamps that | ||
17 | * depict the moments of insertions and deletions on the timeline. | ||
18 | * | ||
19 | * @author Tamas Szabo | ||
20 | * @since 2.4 | ||
21 | * @param <Timestamp> | ||
22 | * the type representing the timestamps | ||
23 | */ | ||
24 | public class Diff<Timestamp extends Comparable<Timestamp>> extends ArrayList<Signed<Timestamp>> { | ||
25 | |||
26 | private static final long serialVersionUID = 3853460426655994160L; | ||
27 | |||
28 | public Diff() { | ||
29 | |||
30 | } | ||
31 | |||
32 | public void appendWithCancellation(Signed<Timestamp> item) { | ||
33 | if (this.isEmpty()) { | ||
34 | this.add(item); | ||
35 | } else { | ||
36 | final Signed<Timestamp> last = this.get(this.size() - 1); | ||
37 | final int lastMinusItem = last.getPayload().compareTo(item.getPayload()); | ||
38 | if (lastMinusItem == 0) { | ||
39 | if (last.getDirection() != item.getDirection()) { | ||
40 | // cancellation | ||
41 | this.remove(this.size() - 1); | ||
42 | } else { | ||
43 | throw new IllegalStateException( | ||
44 | "Trying to insert or delete for the second time at the same timestamp! " + item); | ||
45 | } | ||
46 | } else if (lastMinusItem > 0) { | ||
47 | throw new IllegalStateException( | ||
48 | "Trying to append a timestamp that is smaller than the last one! " + last + " " + item); | ||
49 | } else { | ||
50 | this.add(item); | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
55 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java new file mode 100644 index 00000000..526a95f5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java | |||
@@ -0,0 +1,73 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util.timeline; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
15 | |||
16 | /** | ||
17 | * A timeline which solely consists of one timestamp value, representing a single insertion. Intuitively, a singleton | ||
18 | * timeline always represents a bump which starts at the given timestamp and lasts till plus infinity. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * @since 2.4 | ||
22 | */ | ||
23 | public class SingletonTimeline<Timestamp extends Comparable<Timestamp>> extends Timeline<Timestamp> { | ||
24 | |||
25 | protected final Timestamp start; | ||
26 | |||
27 | SingletonTimeline(final Timestamp timestamp) { | ||
28 | this.start = timestamp; | ||
29 | } | ||
30 | |||
31 | SingletonTimeline(final Diff<Timestamp> diff) { | ||
32 | if (diff.size() != 1 || diff.get(0).getDirection() == Direction.DELETE) { | ||
33 | throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); | ||
34 | } else { | ||
35 | this.start = diff.get(0).getPayload(); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Signed<Timestamp> getSigned(final int index) { | ||
41 | return new Signed<>(Direction.INSERT, this.getUnsigned(index)); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Timestamp getUnsigned(final int index) { | ||
46 | if (index != 0) { | ||
47 | throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); | ||
48 | } else { | ||
49 | return this.start; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public int size() { | ||
55 | return 1; | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public boolean isPresentAtInfinity() { | ||
60 | return true; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Iterable<Signed<Timestamp>> asChangeSequence() { | ||
65 | return Collections.singletonList(this.getSigned(0)); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public boolean isEmpty() { | ||
70 | return false; | ||
71 | } | ||
72 | |||
73 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java new file mode 100644 index 00000000..9214536c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java | |||
@@ -0,0 +1,146 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util.timeline; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
17 | |||
18 | /** | ||
19 | * A timeline describes the life cycle of a piece of data (typically a tuple in a relation) as a sequence of moments. | ||
20 | * Even moments represent appearances, odd moments represent disappearances. A timeline is immutable, once created, it | ||
21 | * is not possible to extend it with further moments. | ||
22 | * | ||
23 | * @author Tamas Szabo | ||
24 | * @since 2.4 | ||
25 | */ | ||
26 | public abstract class Timeline<Timestamp extends Comparable<Timestamp>> { | ||
27 | |||
28 | public abstract Iterable<Signed<Timestamp>> asChangeSequence(); | ||
29 | |||
30 | public abstract boolean isPresentAtInfinity(); | ||
31 | |||
32 | public abstract boolean isEmpty(); | ||
33 | |||
34 | public abstract int size(); | ||
35 | |||
36 | public abstract Signed<Timestamp> getSigned(final int index); | ||
37 | |||
38 | public abstract Timestamp getUnsigned(final int index); | ||
39 | |||
40 | public Timeline<Timestamp> mergeMultiplicative(final Timeline<Timestamp> that) { | ||
41 | final List<Timestamp> result = new ArrayList<>(); | ||
42 | int thisIdx = 0, thatIdx = 0; | ||
43 | Timestamp thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; | ||
44 | Timestamp thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; | ||
45 | |||
46 | while (thisNext != null || thatNext != null) { | ||
47 | int thisMinusThat = 0; | ||
48 | if (thisNext != null && thatNext != null) { | ||
49 | thisMinusThat = thisNext.compareTo(thatNext); | ||
50 | } | ||
51 | if (thisNext == null || thisMinusThat > 0) { | ||
52 | if (thisIdx % 2 == 1) { | ||
53 | result.add(thatNext); | ||
54 | } | ||
55 | thatIdx++; | ||
56 | thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; | ||
57 | } else if (thatNext == null || thisMinusThat < 0) { | ||
58 | if (thatIdx % 2 == 1) { | ||
59 | result.add(thisNext); | ||
60 | } | ||
61 | thisIdx++; | ||
62 | thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; | ||
63 | } else { | ||
64 | if (thisIdx % 2 == thatIdx % 2) { | ||
65 | result.add(thisNext); | ||
66 | } | ||
67 | thisIdx++; | ||
68 | thatIdx++; | ||
69 | thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; | ||
70 | thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return Timelines.createFrom(result); | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * Merges this timeline with the given timestamp diff. The expectation is that the resulting timeline starts with an | ||
79 | * insertion. The logic is similar to a merge sort; we iterate side-by-side over the timeline and the diff. During | ||
80 | * the merge, cancellation can happen if at the same timestamp we observe different signs at the corresponding | ||
81 | * timeline and diff elements. | ||
82 | */ | ||
83 | public Timeline<Timestamp> mergeAdditive(final Diff<Timestamp> diff) { | ||
84 | final Iterator<Signed<Timestamp>> thisItr = this.asChangeSequence().iterator(); | ||
85 | final Iterator<Signed<Timestamp>> diffItr = diff.iterator(); | ||
86 | final List<Timestamp> result = new ArrayList<>(); | ||
87 | Direction expected = Direction.INSERT; | ||
88 | Signed<Timestamp> thisNext = thisItr.hasNext() ? thisItr.next() : null; | ||
89 | Signed<Timestamp> diffNext = diffItr.hasNext() ? diffItr.next() : null; | ||
90 | |||
91 | while (thisNext != null || diffNext != null) { | ||
92 | int thisMinusDiff = 0; | ||
93 | if (thisNext != null && diffNext != null) { | ||
94 | thisMinusDiff = thisNext.getPayload().compareTo(diffNext.getPayload()); | ||
95 | } | ||
96 | |||
97 | if (thisNext == null || thisMinusDiff > 0) { | ||
98 | if (!expected.equals(diffNext.getDirection())) { | ||
99 | throw new IllegalStateException( | ||
100 | String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, | ||
101 | diff, diffNext.getPayload())); | ||
102 | } | ||
103 | result.add(diffNext.getPayload()); | ||
104 | diffNext = diffItr.hasNext() ? diffItr.next() : null; | ||
105 | expected = expected.opposite(); | ||
106 | } else if (diffNext == null || thisMinusDiff < 0) { | ||
107 | if (!expected.equals(thisNext.getDirection())) { | ||
108 | throw new IllegalStateException( | ||
109 | String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, | ||
110 | diff, thisNext.getPayload())); | ||
111 | } | ||
112 | result.add(thisNext.getPayload()); | ||
113 | thisNext = thisItr.hasNext() ? thisItr.next() : null; | ||
114 | expected = expected.opposite(); | ||
115 | } else { | ||
116 | // they cancel out each other | ||
117 | if (diffNext.getDirection().equals(thisNext.getDirection())) { | ||
118 | throw new IllegalStateException(String.format("Changes do not cancel out each other! %s %s @%s", | ||
119 | this, diff, thisNext.getPayload())); | ||
120 | } | ||
121 | diffNext = diffItr.hasNext() ? diffItr.next() : null; | ||
122 | thisNext = thisItr.hasNext() ? thisItr.next() : null; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return Timelines.createFrom(result); | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public String toString() { | ||
131 | final StringBuilder builder = new StringBuilder(); | ||
132 | builder.append("["); | ||
133 | boolean first = true; | ||
134 | for (final Signed<Timestamp> element : this.asChangeSequence()) { | ||
135 | if (first) { | ||
136 | first = false; | ||
137 | } else { | ||
138 | builder.append(", "); | ||
139 | } | ||
140 | builder.append(element.toString()); | ||
141 | } | ||
142 | builder.append("]"); | ||
143 | return builder.toString(); | ||
144 | } | ||
145 | |||
146 | } | ||
diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java new file mode 100644 index 00000000..747fda15 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java | |||
@@ -0,0 +1,46 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. | ||
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.util.timeline; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | /** | ||
14 | * Utility class for creating {@link Timeline}s. | ||
15 | * @author Tamas Szabo | ||
16 | * @since 2.4 | ||
17 | */ | ||
18 | public final class Timelines<Timestamp extends Comparable<Timestamp>> { | ||
19 | |||
20 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createEmpty() { | ||
21 | return new CompactTimeline<Timestamp>(); | ||
22 | } | ||
23 | |||
24 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom( | ||
25 | final Diff<Timestamp> diffs) { | ||
26 | if (diffs.size() == 1) { | ||
27 | return new SingletonTimeline<Timestamp>(diffs); | ||
28 | } else { | ||
29 | return new CompactTimeline<Timestamp>(diffs); | ||
30 | } | ||
31 | } | ||
32 | |||
33 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom( | ||
34 | final List<Timestamp> timestamps) { | ||
35 | if (timestamps.size() == 1) { | ||
36 | return new SingletonTimeline<Timestamp>(timestamps.get(0)); | ||
37 | } else { | ||
38 | return new CompactTimeline<Timestamp>(timestamps); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | public static <Timestamp extends Comparable<Timestamp>> Timeline<Timestamp> createFrom(final Timestamp timestamp) { | ||
43 | return new SingletonTimeline<Timestamp>(timestamp); | ||
44 | } | ||
45 | |||
46 | } | ||