diff options
Diffstat (limited to 'subprojects/viatra-runtime-rete/src/main/java/tools')
180 files changed, 22765 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java new file mode 100644 index 00000000..2588bde1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java | |||
@@ -0,0 +1,474 @@ | |||
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.rete.aggregation; | ||
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.Objects; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
25 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
26 | import tools.refinery.viatra.runtime.rete.index.StandardIndexer; | ||
27 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
28 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
29 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
31 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
32 | import tools.refinery.viatra.runtime.rete.single.SingleInputNode; | ||
33 | |||
34 | /** | ||
35 | * Groups incoming tuples by the given mask, and aggregates values at a specific index in each group. | ||
36 | * <p> | ||
37 | * Direct children are not supported, use via outer join indexers instead. | ||
38 | * <p> | ||
39 | * There are both timeless and timely implementations. | ||
40 | * | ||
41 | * @author Tamas Szabo | ||
42 | * @since 2.2 | ||
43 | * | ||
44 | */ | ||
45 | public abstract class AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends SingleInputNode | ||
46 | implements Clearable, IAggregatorNode { | ||
47 | |||
48 | /** | ||
49 | * @since 1.6 | ||
50 | */ | ||
51 | protected final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator; | ||
52 | |||
53 | /** | ||
54 | * @since 1.6 | ||
55 | */ | ||
56 | protected final TupleMask groupMask; | ||
57 | |||
58 | /** | ||
59 | * @since 1.6 | ||
60 | */ | ||
61 | protected final TupleMask columnMask; | ||
62 | |||
63 | /** | ||
64 | * @since 1.6 | ||
65 | */ | ||
66 | protected final int sourceWidth; | ||
67 | |||
68 | /** | ||
69 | * @since 1.6 | ||
70 | */ | ||
71 | protected final IQueryRuntimeContext runtimeContext; | ||
72 | |||
73 | protected final AggregateResult NEUTRAL; | ||
74 | |||
75 | protected AggregatorOuterIndexer aggregatorOuterIndexer; | ||
76 | |||
77 | @SuppressWarnings("rawtypes") | ||
78 | protected AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers; | ||
79 | |||
80 | /** | ||
81 | * Creates a new column aggregator node. | ||
82 | * | ||
83 | * @param reteContainer | ||
84 | * the RETE container of the node | ||
85 | * @param operator | ||
86 | * the aggregation operator | ||
87 | * @param deleteRederiveEvaluation | ||
88 | * true if the node should run in DRED mode, false otherwise | ||
89 | * @param groupMask | ||
90 | * the mask that masks a tuple to obtain the key that we are grouping-by | ||
91 | * @param columnMask | ||
92 | * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over | ||
93 | * @param posetComparator | ||
94 | * the poset comparator for the column, if known, otherwise it can be null | ||
95 | * @since 1.6 | ||
96 | */ | ||
97 | public AbstractColumnAggregatorNode(final ReteContainer reteContainer, | ||
98 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
99 | final TupleMask groupMask, final TupleMask columnMask) { | ||
100 | super(reteContainer); | ||
101 | this.operator = operator; | ||
102 | this.groupMask = groupMask; | ||
103 | this.columnMask = columnMask; | ||
104 | this.sourceWidth = groupMask.indices.length; | ||
105 | this.runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext(); | ||
106 | this.NEUTRAL = operator.getAggregate(operator.createNeutral()); | ||
107 | reteContainer.registerClearable(this); | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Creates a new column aggregator node. | ||
112 | * | ||
113 | * @param reteContainer | ||
114 | * the RETE container of the node | ||
115 | * @param operator | ||
116 | * the aggregation operator | ||
117 | * @param groupMask | ||
118 | * the mask that masks a tuple to obtain the key that we are grouping-by | ||
119 | * @param aggregatedColumn | ||
120 | * the index of the column that the aggregator node is aggregating over | ||
121 | */ | ||
122 | public AbstractColumnAggregatorNode(final ReteContainer reteContainer, | ||
123 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
124 | final TupleMask groupMask, final int aggregatedColumn) { | ||
125 | this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth)); | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public CommunicationTracker getCommunicationTracker() { | ||
130 | return this.reteContainer.getCommunicationTracker(); | ||
131 | } | ||
132 | |||
133 | @Override | ||
134 | public void pullInto(Collection<Tuple> collector, boolean flush) { | ||
135 | // DIRECT CHILDREN NOT SUPPORTED | ||
136 | throw new UnsupportedOperationException(); | ||
137 | } | ||
138 | |||
139 | @Override | ||
140 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
141 | // DIRECT CHILDREN NOT SUPPORTED | ||
142 | throw new UnsupportedOperationException(); | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public void appendChild(Receiver receiver) { | ||
147 | // DIRECT CHILDREN NOT SUPPORTED | ||
148 | throw new UnsupportedOperationException(); | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public Indexer getAggregatorOuterIndexer() { | ||
153 | if (aggregatorOuterIndexer == null) { | ||
154 | aggregatorOuterIndexer = new AggregatorOuterIndexer(); | ||
155 | this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer); | ||
156 | } | ||
157 | return aggregatorOuterIndexer; | ||
158 | } | ||
159 | |||
160 | @Override | ||
161 | public Indexer getAggregatorOuterIdentityIndexer(final int resultPositionInSignature) { | ||
162 | if (aggregatorOuterIdentityIndexers == null) { | ||
163 | aggregatorOuterIdentityIndexers = new AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[sourceWidth | ||
164 | + 1]; | ||
165 | } | ||
166 | if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) { | ||
167 | aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer( | ||
168 | resultPositionInSignature); | ||
169 | this.getCommunicationTracker().registerDependency(this, | ||
170 | aggregatorOuterIdentityIndexers[resultPositionInSignature]); | ||
171 | } | ||
172 | return aggregatorOuterIdentityIndexers[resultPositionInSignature]; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * @since 2.4 | ||
177 | */ | ||
178 | public void propagateAggregateResultUpdate(final Tuple group, final AggregateResult oldValue, | ||
179 | final AggregateResult newValue, final Timestamp timestamp) { | ||
180 | if (!Objects.equals(oldValue, newValue)) { | ||
181 | propagate(Direction.DELETE, group, oldValue, timestamp); | ||
182 | propagate(Direction.INSERT, group, newValue, timestamp); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @since 2.4 | ||
188 | */ | ||
189 | @SuppressWarnings("unchecked") | ||
190 | public void propagate(final Direction direction, final Tuple group, final AggregateResult value, | ||
191 | final Timestamp timestamp) { | ||
192 | final Tuple tuple = tupleFromAggregateResult(group, value); | ||
193 | |||
194 | if (aggregatorOuterIndexer != null) { | ||
195 | aggregatorOuterIndexer.propagate(direction, tuple, group, timestamp); | ||
196 | } | ||
197 | if (aggregatorOuterIdentityIndexers != null) { | ||
198 | for (final AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers) { | ||
199 | if (aggregatorOuterIdentityIndexer != null) { | ||
200 | aggregatorOuterIdentityIndexer.propagate(direction, tuple, group, timestamp); | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | public abstract Tuple getAggregateTuple(final Tuple key); | ||
207 | |||
208 | /** | ||
209 | * @since 2.4 | ||
210 | */ | ||
211 | public abstract Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple key); | ||
212 | |||
213 | public abstract AggregateResult getAggregateResult(final Tuple key); | ||
214 | |||
215 | /** | ||
216 | * @since 2.4 | ||
217 | */ | ||
218 | public abstract Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple key); | ||
219 | |||
220 | protected Tuple tupleFromAggregateResult(final Tuple groupTuple, final AggregateResult aggregateResult) { | ||
221 | if (aggregateResult == null) { | ||
222 | return null; | ||
223 | } else { | ||
224 | return Tuples.staticArityLeftInheritanceTupleOf(groupTuple, runtimeContext.wrapElement(aggregateResult)); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original | ||
230 | * signature. | ||
231 | * | ||
232 | * @author Gabor Bergmann | ||
233 | * @author Tamas Szabo | ||
234 | * | ||
235 | */ | ||
236 | protected class AggregatorOuterIndexer extends StandardIndexer { | ||
237 | |||
238 | /** | ||
239 | * @since 2.4 | ||
240 | */ | ||
241 | protected NetworkStructureChangeSensitiveLogic logic; | ||
242 | |||
243 | public AggregatorOuterIndexer() { | ||
244 | super(AbstractColumnAggregatorNode.this.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1)); | ||
245 | this.parent = AbstractColumnAggregatorNode.this; | ||
246 | this.logic = createLogic(); | ||
247 | } | ||
248 | |||
249 | @Override | ||
250 | public void networkStructureChanged() { | ||
251 | super.networkStructureChanged(); | ||
252 | this.logic = createLogic(); | ||
253 | } | ||
254 | |||
255 | @Override | ||
256 | public Collection<Tuple> get(final Tuple signature) { | ||
257 | return this.logic.get(signature); | ||
258 | } | ||
259 | |||
260 | @Override | ||
261 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) { | ||
262 | return this.logic.getTimeline(signature); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * @since 2.4 | ||
267 | */ | ||
268 | public void propagate(final Direction direction, final Tuple tuple, final Tuple group, | ||
269 | final Timestamp timestamp) { | ||
270 | if (tuple != null) { | ||
271 | propagate(direction, tuple, group, true, timestamp); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | @Override | ||
276 | public Node getActiveNode() { | ||
277 | return AbstractColumnAggregatorNode.this; | ||
278 | } | ||
279 | |||
280 | /** | ||
281 | * @since 2.4 | ||
282 | */ | ||
283 | protected NetworkStructureChangeSensitiveLogic createLogic() { | ||
284 | if (this.reteContainer.isTimelyEvaluation() | ||
285 | && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
286 | return this.TIMELY; | ||
287 | } else { | ||
288 | return this.TIMELESS; | ||
289 | } | ||
290 | } | ||
291 | |||
292 | private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { | ||
293 | |||
294 | @Override | ||
295 | public Collection<Tuple> get(final Tuple signature) { | ||
296 | final Tuple aggregateTuple = getAggregateTuple(signature); | ||
297 | if (aggregateTuple == null) { | ||
298 | return null; | ||
299 | } else { | ||
300 | return Collections.singleton(aggregateTuple); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | @Override | ||
305 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) { | ||
306 | throw new UnsupportedOperationException(); | ||
307 | } | ||
308 | |||
309 | }; | ||
310 | |||
311 | private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { | ||
312 | |||
313 | @Override | ||
314 | public Collection<Tuple> get(final Tuple signatureWithResult) { | ||
315 | return TIMELESS.get(signatureWithResult); | ||
316 | } | ||
317 | |||
318 | @Override | ||
319 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) { | ||
320 | final Map<Tuple, Timeline<Timestamp>> aggregateTuples = getAggregateTupleTimeline(signature); | ||
321 | if (aggregateTuples.isEmpty()) { | ||
322 | return null; | ||
323 | } else { | ||
324 | return aggregateTuples; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | }; | ||
329 | |||
330 | } | ||
331 | |||
332 | /** | ||
333 | * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for | ||
334 | * this index is the original 'group by' masked tuple, with the suspected result inserted at position | ||
335 | * resultPositionInSignature. | ||
336 | * | ||
337 | * @author Gabor Bergmann | ||
338 | * @author Tamas Szabo | ||
339 | * | ||
340 | */ | ||
341 | protected class AggregatorOuterIdentityIndexer extends StandardIndexer { | ||
342 | |||
343 | protected final int resultPositionInSignature; | ||
344 | protected final TupleMask pruneResult; | ||
345 | protected final TupleMask reorderMask; | ||
346 | /** | ||
347 | * @since 2.4 | ||
348 | */ | ||
349 | protected NetworkStructureChangeSensitiveLogic logic; | ||
350 | |||
351 | public AggregatorOuterIdentityIndexer(final int resultPositionInSignature) { | ||
352 | super(AbstractColumnAggregatorNode.this.reteContainer, | ||
353 | TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1)); | ||
354 | this.resultPositionInSignature = resultPositionInSignature; | ||
355 | this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1); | ||
356 | if (resultPositionInSignature == sourceWidth) { | ||
357 | this.reorderMask = null; | ||
358 | } else { | ||
359 | this.reorderMask = mask; | ||
360 | } | ||
361 | this.logic = createLogic(); | ||
362 | } | ||
363 | |||
364 | @Override | ||
365 | public void networkStructureChanged() { | ||
366 | super.networkStructureChanged(); | ||
367 | this.logic = createLogic(); | ||
368 | } | ||
369 | |||
370 | @Override | ||
371 | public Collection<Tuple> get(final Tuple signatureWithResult) { | ||
372 | return this.logic.get(signatureWithResult); | ||
373 | } | ||
374 | |||
375 | /** | ||
376 | * @since 2.4 | ||
377 | */ | ||
378 | @Override | ||
379 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) { | ||
380 | return this.logic.getTimeline(signature); | ||
381 | } | ||
382 | |||
383 | /** | ||
384 | * @since 2.4 | ||
385 | */ | ||
386 | public void propagate(final Direction direction, final Tuple tuple, final Tuple group, | ||
387 | final Timestamp timestamp) { | ||
388 | if (tuple != null) { | ||
389 | propagate(direction, reorder(tuple), group, true, timestamp); | ||
390 | } | ||
391 | } | ||
392 | |||
393 | private Tuple reorder(final Tuple signatureWithResult) { | ||
394 | Tuple transformed; | ||
395 | if (reorderMask == null) { | ||
396 | transformed = signatureWithResult; | ||
397 | } else { | ||
398 | transformed = reorderMask.transform(signatureWithResult); | ||
399 | } | ||
400 | return transformed; | ||
401 | } | ||
402 | |||
403 | @Override | ||
404 | public Node getActiveNode() { | ||
405 | return this.parent; | ||
406 | } | ||
407 | |||
408 | /** | ||
409 | * @since 2.4 | ||
410 | */ | ||
411 | protected NetworkStructureChangeSensitiveLogic createLogic() { | ||
412 | if (this.reteContainer.isTimelyEvaluation() | ||
413 | && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
414 | return this.TIMELY; | ||
415 | } else { | ||
416 | return this.TIMELESS; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { | ||
421 | |||
422 | @Override | ||
423 | public Collection<Tuple> get(final Tuple signatureWithResult) { | ||
424 | final Tuple prunedSignature = pruneResult.transform(signatureWithResult); | ||
425 | final AggregateResult result = getAggregateResult(prunedSignature); | ||
426 | if (result != null && Objects.equals(signatureWithResult.get(resultPositionInSignature), result)) { | ||
427 | return Collections.singleton(signatureWithResult); | ||
428 | } else { | ||
429 | return null; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | @Override | ||
434 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) { | ||
435 | throw new UnsupportedOperationException(); | ||
436 | } | ||
437 | |||
438 | }; | ||
439 | |||
440 | private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { | ||
441 | |||
442 | @Override | ||
443 | public Collection<Tuple> get(final Tuple signatureWithResult) { | ||
444 | return TIMELESS.get(signatureWithResult); | ||
445 | } | ||
446 | |||
447 | @Override | ||
448 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signatureWithResult) { | ||
449 | final Tuple prunedSignature = pruneResult.transform(signatureWithResult); | ||
450 | final Map<AggregateResult, Timeline<Timestamp>> result = getAggregateResultTimeline(prunedSignature); | ||
451 | for (final Entry<AggregateResult, Timeline<Timestamp>> entry : result.entrySet()) { | ||
452 | if (Objects.equals(signatureWithResult.get(resultPositionInSignature), entry.getKey())) { | ||
453 | return Collections.singletonMap(signatureWithResult, entry.getValue()); | ||
454 | } | ||
455 | } | ||
456 | return null; | ||
457 | } | ||
458 | |||
459 | }; | ||
460 | |||
461 | } | ||
462 | |||
463 | /** | ||
464 | * @since 2.4 | ||
465 | */ | ||
466 | protected static abstract class NetworkStructureChangeSensitiveLogic { | ||
467 | |||
468 | public abstract Collection<Tuple> get(final Tuple signatureWithResult); | ||
469 | |||
470 | public abstract Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature); | ||
471 | |||
472 | } | ||
473 | |||
474 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java new file mode 100644 index 00000000..4480aed8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java | |||
@@ -0,0 +1,369 @@ | |||
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.rete.aggregation; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.Map.Entry; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
15 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
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.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
21 | import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver; | ||
22 | import tools.refinery.viatra.runtime.rete.network.RederivableNode; | ||
23 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
26 | import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup; | ||
27 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
28 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
29 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox; | ||
30 | |||
31 | /** | ||
32 | * Timeless implementation of the column aggregator node. | ||
33 | * <p> | ||
34 | * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the | ||
35 | * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached | ||
36 | * during the evaluation. | ||
37 | * | ||
38 | * @author Gabor Bergmann | ||
39 | * @author Tamas Szabo | ||
40 | * @since 1.4 | ||
41 | */ | ||
42 | public class ColumnAggregatorNode<Domain, Accumulator, AggregateResult> | ||
43 | extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> | ||
44 | implements RederivableNode, PosetAwareReceiver { | ||
45 | |||
46 | /** | ||
47 | * @since 1.6 | ||
48 | */ | ||
49 | protected final IPosetComparator posetComparator; | ||
50 | |||
51 | /** | ||
52 | * @since 1.6 | ||
53 | */ | ||
54 | protected final boolean deleteRederiveEvaluation; | ||
55 | |||
56 | // invariant: neutral values are not stored | ||
57 | /** | ||
58 | * @since 1.6 | ||
59 | */ | ||
60 | protected final Map<Tuple, Accumulator> memory; | ||
61 | /** | ||
62 | * @since 1.6 | ||
63 | */ | ||
64 | protected final Map<Tuple, Accumulator> rederivableMemory; | ||
65 | |||
66 | /** | ||
67 | * @since 1.7 | ||
68 | */ | ||
69 | protected CommunicationGroup currentGroup; | ||
70 | |||
71 | /** | ||
72 | * Creates a new column aggregator node. | ||
73 | * | ||
74 | * @param reteContainer | ||
75 | * the RETE container of the node | ||
76 | * @param operator | ||
77 | * the aggregation operator | ||
78 | * @param deleteRederiveEvaluation | ||
79 | * true if the node should run in DRED mode, false otherwise | ||
80 | * @param groupMask | ||
81 | * the mask that masks a tuple to obtain the key that we are grouping-by | ||
82 | * @param columnMask | ||
83 | * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over | ||
84 | * @param posetComparator | ||
85 | * the poset comparator for the column, if known, otherwise it can be null | ||
86 | * @since 1.6 | ||
87 | */ | ||
88 | public ColumnAggregatorNode(final ReteContainer reteContainer, | ||
89 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
90 | final boolean deleteRederiveEvaluation, final TupleMask groupMask, final TupleMask columnMask, | ||
91 | final IPosetComparator posetComparator) { | ||
92 | super(reteContainer, operator, groupMask, columnMask); | ||
93 | this.memory = CollectionsFactory.createMap(); | ||
94 | this.rederivableMemory = CollectionsFactory.createMap(); | ||
95 | this.deleteRederiveEvaluation = deleteRederiveEvaluation; | ||
96 | this.posetComparator = posetComparator; | ||
97 | // mailbox MUST be instantiated after the fields are all set | ||
98 | this.mailbox = instantiateMailbox(); | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Creates a new column aggregator node. | ||
103 | * | ||
104 | * @param reteContainer | ||
105 | * the RETE container of the node | ||
106 | * @param operator | ||
107 | * the aggregation operator | ||
108 | * @param groupMask | ||
109 | * the mask that masks a tuple to obtain the key that we are grouping-by | ||
110 | * @param aggregatedColumn | ||
111 | * the index of the column that the aggregator node is aggregating over | ||
112 | */ | ||
113 | public ColumnAggregatorNode(final ReteContainer reteContainer, | ||
114 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
115 | final TupleMask groupMask, final int aggregatedColumn) { | ||
116 | this(reteContainer, operator, false, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth), | ||
117 | null); | ||
118 | } | ||
119 | |||
120 | @Override | ||
121 | public boolean isInDRedMode() { | ||
122 | return this.deleteRederiveEvaluation; | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | protected Mailbox instantiateMailbox() { | ||
127 | if (groupMask != null && columnMask != null && posetComparator != null) { | ||
128 | return new PosetAwareMailbox(this, this.reteContainer); | ||
129 | } else { | ||
130 | return new BehaviorChangingMailbox(this, this.reteContainer); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | @Override | ||
135 | public TupleMask getCoreMask() { | ||
136 | return groupMask; | ||
137 | } | ||
138 | |||
139 | @Override | ||
140 | public TupleMask getPosetMask() { | ||
141 | return columnMask; | ||
142 | } | ||
143 | |||
144 | @Override | ||
145 | public IPosetComparator getPosetComparator() { | ||
146 | return posetComparator; | ||
147 | } | ||
148 | |||
149 | @Override | ||
150 | public void rederiveOne() { | ||
151 | final Entry<Tuple, Accumulator> entry = rederivableMemory.entrySet().iterator().next(); | ||
152 | final Tuple group = entry.getKey(); | ||
153 | final Accumulator accumulator = entry.getValue(); | ||
154 | rederivableMemory.remove(group); | ||
155 | memory.put(group, accumulator); | ||
156 | // unregister the node if there is nothing left to be re-derived | ||
157 | if (this.rederivableMemory.isEmpty()) { | ||
158 | ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); | ||
159 | } | ||
160 | final AggregateResult value = operator.getAggregate(accumulator); | ||
161 | propagateAggregateResultUpdate(group, NEUTRAL, value, Timestamp.ZERO); | ||
162 | } | ||
163 | |||
164 | @Override | ||
165 | public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) { | ||
166 | if (this.deleteRederiveEvaluation) { | ||
167 | updateWithDeleteAndRederive(direction, update, monotone); | ||
168 | } else { | ||
169 | updateDefault(direction, update, Timestamp.ZERO); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | @Override | ||
174 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
175 | updateWithPosetInfo(direction, update, false); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * @since 2.4 | ||
180 | */ | ||
181 | protected void updateDefault(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
182 | final Tuple key = groupMask.transform(update); | ||
183 | final Tuple value = columnMask.transform(update); | ||
184 | @SuppressWarnings("unchecked") | ||
185 | final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0)); | ||
186 | final boolean isInsertion = direction == Direction.INSERT; | ||
187 | |||
188 | final Accumulator oldMainAccumulator = getMainAccumulator(key); | ||
189 | final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator); | ||
190 | |||
191 | final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, isInsertion); | ||
192 | storeIfNotNeutral(key, newMainAccumulator, memory); | ||
193 | final AggregateResult newValue = operator.getAggregate(newMainAccumulator); | ||
194 | |||
195 | propagateAggregateResultUpdate(key, oldValue, newValue, timestamp); | ||
196 | } | ||
197 | |||
198 | /** | ||
199 | * @since 2.4 | ||
200 | */ | ||
201 | protected void updateWithDeleteAndRederive(final Direction direction, final Tuple update, final boolean monotone) { | ||
202 | final Tuple group = groupMask.transform(update); | ||
203 | final Tuple value = columnMask.transform(update); | ||
204 | @SuppressWarnings("unchecked") | ||
205 | final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0)); | ||
206 | final boolean isInsertion = direction == Direction.INSERT; | ||
207 | |||
208 | Accumulator oldMainAccumulator = memory.get(group); | ||
209 | Accumulator oldRederivableAccumulator = rederivableMemory.get(group); | ||
210 | |||
211 | if (direction == Direction.INSERT) { | ||
212 | // INSERT | ||
213 | if (oldRederivableAccumulator != null) { | ||
214 | // the group is in the re-derivable memory | ||
215 | final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator, | ||
216 | aggregableValue, isInsertion); | ||
217 | storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory); | ||
218 | if (rederivableMemory.isEmpty()) { | ||
219 | // there is nothing left to be re-derived | ||
220 | // this can happen if the accumulator became neutral in response to the INSERT | ||
221 | ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); | ||
222 | } | ||
223 | } else { | ||
224 | // the group is in the main memory | ||
225 | // at this point, it can happen that we need to initialize with a neutral accumulator | ||
226 | if (oldMainAccumulator == null) { | ||
227 | oldMainAccumulator = operator.createNeutral(); | ||
228 | } | ||
229 | |||
230 | final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator); | ||
231 | final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, | ||
232 | isInsertion); | ||
233 | storeIfNotNeutral(group, newMainAccumulator, memory); | ||
234 | final AggregateResult newValue = operator.getAggregate(newMainAccumulator); | ||
235 | propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO); | ||
236 | } | ||
237 | } else { | ||
238 | // DELETE | ||
239 | if (oldRederivableAccumulator != null) { | ||
240 | // the group is in the re-derivable memory | ||
241 | if (oldMainAccumulator != null) { | ||
242 | issueError("[INTERNAL ERROR] Inconsistent state for " + update | ||
243 | + " because it is present both in the main and re-derivable memory in the ColumnAggregatorNode " | ||
244 | + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null); | ||
245 | } | ||
246 | try { | ||
247 | final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator, | ||
248 | aggregableValue, isInsertion); | ||
249 | storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory); | ||
250 | if (rederivableMemory.isEmpty()) { | ||
251 | // there is nothing left to be re-derived | ||
252 | // this can happen if the accumulator became neutral in response to the DELETE | ||
253 | ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); | ||
254 | } | ||
255 | } catch (final NullPointerException ex) { | ||
256 | issueError("[INTERNAL ERROR] Deleting a domain element in " + update | ||
257 | + " which did not exist before in ColumnAggregatorNode " + this + " for pattern(s) " | ||
258 | + getTraceInfoPatternsEnumerated(), ex); | ||
259 | } | ||
260 | } else { | ||
261 | // the group is in the main memory | ||
262 | // at this point, it can happen that we need to initialize with a neutral accumulator | ||
263 | if (oldMainAccumulator == null) { | ||
264 | oldMainAccumulator = operator.createNeutral(); | ||
265 | } | ||
266 | |||
267 | final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator); | ||
268 | final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, | ||
269 | isInsertion); | ||
270 | final AggregateResult newValue = operator.getAggregate(newMainAccumulator); | ||
271 | |||
272 | if (monotone) { | ||
273 | storeIfNotNeutral(group, newMainAccumulator, memory); | ||
274 | propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO); | ||
275 | } else { | ||
276 | final boolean wasEmpty = rederivableMemory.isEmpty(); | ||
277 | if (storeIfNotNeutral(group, newMainAccumulator, rederivableMemory) && wasEmpty) { | ||
278 | ((RecursiveCommunicationGroup) currentGroup).addRederivable(this); | ||
279 | } | ||
280 | memory.remove(group); | ||
281 | propagateAggregateResultUpdate(group, oldValue, NEUTRAL, Timestamp.ZERO); | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | } | ||
286 | |||
287 | @Override | ||
288 | public void clear() { | ||
289 | this.memory.clear(); | ||
290 | this.rederivableMemory.clear(); | ||
291 | this.childMailboxes.clear(); | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * Returns true if the accumulator was stored, false otherwise. | ||
296 | * | ||
297 | * @since 1.6 | ||
298 | */ | ||
299 | protected boolean storeIfNotNeutral(final Tuple key, final Accumulator accumulator, | ||
300 | final Map<Tuple, Accumulator> memory) { | ||
301 | if (operator.isNeutral(accumulator)) { | ||
302 | memory.remove(key); | ||
303 | return false; | ||
304 | } else { | ||
305 | memory.put(key, accumulator); | ||
306 | return true; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | @Override | ||
311 | public Tuple getAggregateTuple(final Tuple group) { | ||
312 | final Accumulator accumulator = getMainAccumulator(group); | ||
313 | final AggregateResult result = operator.getAggregate(accumulator); | ||
314 | return tupleFromAggregateResult(group, result); | ||
315 | } | ||
316 | |||
317 | @Override | ||
318 | public AggregateResult getAggregateResult(final Tuple group) { | ||
319 | final Accumulator accumulator = getMainAccumulator(group); | ||
320 | return operator.getAggregate(accumulator); | ||
321 | } | ||
322 | |||
323 | @Override | ||
324 | public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(Tuple key) { | ||
325 | throw new UnsupportedOperationException(); | ||
326 | } | ||
327 | |||
328 | @Override | ||
329 | public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(Tuple key) { | ||
330 | throw new UnsupportedOperationException(); | ||
331 | } | ||
332 | |||
333 | /** | ||
334 | * @since 1.6 | ||
335 | */ | ||
336 | protected Accumulator getMainAccumulator(final Tuple key) { | ||
337 | return getAccumulator(key, memory); | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * @since 1.6 | ||
342 | */ | ||
343 | protected Accumulator getRederivableAccumulator(final Tuple key) { | ||
344 | return getAccumulator(key, rederivableMemory); | ||
345 | } | ||
346 | |||
347 | /** | ||
348 | * @since 1.6 | ||
349 | */ | ||
350 | protected Accumulator getAccumulator(final Tuple key, final Map<Tuple, Accumulator> memory) { | ||
351 | Accumulator accumulator = memory.get(key); | ||
352 | if (accumulator == null) { | ||
353 | return operator.createNeutral(); | ||
354 | } else { | ||
355 | return accumulator; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | @Override | ||
360 | public CommunicationGroup getCurrentGroup() { | ||
361 | return currentGroup; | ||
362 | } | ||
363 | |||
364 | @Override | ||
365 | public void setCurrentGroup(final CommunicationGroup currentGroup) { | ||
366 | this.currentGroup = currentGroup; | ||
367 | } | ||
368 | |||
369 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java new file mode 100644 index 00000000..7c98de0d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java | |||
@@ -0,0 +1,38 @@ | |||
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.rete.aggregation; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
16 | |||
17 | /** | ||
18 | * An aggregation node that simply counts the number of tuples conforming to the signature. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * @since 1.4 | ||
22 | */ | ||
23 | public class CountNode extends IndexerBasedAggregatorNode { | ||
24 | |||
25 | public CountNode(ReteContainer reteContainer) { | ||
26 | super(reteContainer); | ||
27 | } | ||
28 | |||
29 | int sizeOf(Collection<Tuple> group) { | ||
30 | return group == null ? 0 : group.size(); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public Object aggregateGroup(Tuple signature, Collection<Tuple> group) { | ||
35 | return sizeOf(group); | ||
36 | } | ||
37 | |||
38 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java new file mode 100644 index 00000000..3c7850de --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java | |||
@@ -0,0 +1,120 @@ | |||
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.rete.aggregation; | ||
10 | |||
11 | import java.util.AbstractMap.SimpleEntry; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | |||
20 | /** | ||
21 | * An optimized {@link Map} implementation where each key is produced by joining together a group tuple and some other | ||
22 | * object (via left inheritance). Only a select few {@link Map} operations are supported. This collection is | ||
23 | * unmodifiable. | ||
24 | * | ||
25 | * Operations on this map assume that client queries also obey the contract that keys are constructed from a group tuple | ||
26 | * and an additional object. | ||
27 | * | ||
28 | * @author Tamas Szabo | ||
29 | * @since 2.4 | ||
30 | */ | ||
31 | public class GroupedMap<GroupedKeyType, ValueType> implements Map<Tuple, ValueType> { | ||
32 | |||
33 | protected final Tuple group; | ||
34 | // cached group size value is to be used in get() | ||
35 | private final int groupSize; | ||
36 | protected final Map<GroupedKeyType, ValueType> mappings; | ||
37 | protected final IQueryRuntimeContext runtimeContext; | ||
38 | |||
39 | public GroupedMap(final Tuple group, final Map<GroupedKeyType, ValueType> mappings, | ||
40 | final IQueryRuntimeContext runtimeContext) { | ||
41 | this.group = group; | ||
42 | this.groupSize = group.getSize(); | ||
43 | this.mappings = mappings; | ||
44 | this.runtimeContext = runtimeContext; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public int size() { | ||
49 | return this.mappings.size(); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public boolean isEmpty() { | ||
54 | return this.mappings.isEmpty(); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public boolean containsKey(final Object key) { | ||
59 | throw new UnsupportedOperationException(); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public boolean containsValue(final Object value) { | ||
64 | return this.mappings.containsValue(value); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public ValueType get(final Object key) { | ||
69 | if (key instanceof Tuple) { | ||
70 | final Object value = ((Tuple) key).get(this.groupSize); | ||
71 | final Object unwrappedValue = this.runtimeContext.unwrapElement(value); | ||
72 | return this.mappings.get(unwrappedValue); | ||
73 | } else { | ||
74 | return null; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public ValueType put(final Tuple key, final ValueType value) { | ||
80 | throw new UnsupportedOperationException(); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public ValueType remove(final Object key) { | ||
85 | throw new UnsupportedOperationException(); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public void putAll(final Map<? extends Tuple, ? extends ValueType> map) { | ||
90 | throw new UnsupportedOperationException(); | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | public void clear() { | ||
95 | throw new UnsupportedOperationException(); | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public Set<Tuple> keySet() { | ||
100 | return new GroupedSet<Tuple, GroupedKeyType, Tuple>(this.group, this.mappings.keySet(), (g, v) -> { | ||
101 | return Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v)); | ||
102 | }); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public Collection<ValueType> values() { | ||
107 | return this.mappings.values(); | ||
108 | } | ||
109 | |||
110 | @Override | ||
111 | public Set<Entry<Tuple, ValueType>> entrySet() { | ||
112 | return new GroupedSet<Tuple, GroupedKeyType, Entry<Tuple, ValueType>>(this.group, this.mappings.keySet(), | ||
113 | (g, v) -> { | ||
114 | final Tuple key = Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v)); | ||
115 | final ValueType value = this.mappings.get(v); | ||
116 | return new SimpleEntry<Tuple, ValueType>(key, value); | ||
117 | }); | ||
118 | } | ||
119 | |||
120 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java new file mode 100644 index 00000000..65561e53 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java | |||
@@ -0,0 +1,114 @@ | |||
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.rete.aggregation; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.BiFunction; | ||
15 | |||
16 | /** | ||
17 | * An optimized {@link Set} implementation where each contained value is produced by combining together a grouping value | ||
18 | * and some other (key) object. The way of combining together these two values is specified by the closure passed to the | ||
19 | * constructor. Only a select few {@link Set} operations are supported. This collection is unmodifiable. | ||
20 | * | ||
21 | * @author Tamas Szabo | ||
22 | * @since 2.4 | ||
23 | */ | ||
24 | public class GroupedSet<GroupingValueType, GroupedKeyType, WholeKeyType> implements Set<WholeKeyType> { | ||
25 | |||
26 | protected final GroupingValueType group; | ||
27 | protected final Collection<GroupedKeyType> values; | ||
28 | protected final BiFunction<GroupingValueType, GroupedKeyType, WholeKeyType> valueFunc; | ||
29 | |||
30 | public GroupedSet(final GroupingValueType group, final Collection<GroupedKeyType> values, | ||
31 | final BiFunction<GroupingValueType, GroupedKeyType, WholeKeyType> valueFunc) { | ||
32 | this.group = group; | ||
33 | this.values = values; | ||
34 | this.valueFunc = valueFunc; | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public int size() { | ||
39 | return this.values.size(); | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public boolean isEmpty() { | ||
44 | return this.values.isEmpty(); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public boolean contains(final Object obj) { | ||
49 | throw new UnsupportedOperationException(); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public Iterator<WholeKeyType> iterator() { | ||
54 | final Iterator<GroupedKeyType> wrapped = this.values.iterator(); | ||
55 | return new Iterator<WholeKeyType>() { | ||
56 | @Override | ||
57 | public boolean hasNext() { | ||
58 | return wrapped.hasNext(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public WholeKeyType next() { | ||
63 | final GroupedKeyType value = wrapped.next(); | ||
64 | return valueFunc.apply(group, value); | ||
65 | } | ||
66 | }; | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public Object[] toArray() { | ||
71 | throw new UnsupportedOperationException(); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public <T> T[] toArray(final T[] arr) { | ||
76 | throw new UnsupportedOperationException(); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public boolean add(final WholeKeyType tuple) { | ||
81 | throw new UnsupportedOperationException(); | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public boolean remove(final Object obj) { | ||
86 | throw new UnsupportedOperationException(); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public boolean containsAll(final Collection<?> c) { | ||
91 | throw new UnsupportedOperationException(); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public boolean addAll(final Collection<? extends WholeKeyType> coll) { | ||
96 | throw new UnsupportedOperationException(); | ||
97 | } | ||
98 | |||
99 | @Override | ||
100 | public boolean retainAll(final Collection<?> coll) { | ||
101 | throw new UnsupportedOperationException(); | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public boolean removeAll(final Collection<?> coll) { | ||
106 | throw new UnsupportedOperationException(); | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public void clear() { | ||
111 | throw new UnsupportedOperationException(); | ||
112 | } | ||
113 | |||
114 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java new file mode 100644 index 00000000..6c286364 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java | |||
@@ -0,0 +1,26 @@ | |||
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.rete.aggregation; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
12 | |||
13 | /** | ||
14 | * Expresses that aggregators expose specialized non-enumerable indexers for outer joining. | ||
15 | * @author Gabor Bergmann | ||
16 | * | ||
17 | * @since 1.4 | ||
18 | * | ||
19 | */ | ||
20 | public interface IAggregatorNode { | ||
21 | |||
22 | Indexer getAggregatorOuterIndexer(); | ||
23 | |||
24 | Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature); | ||
25 | |||
26 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java new file mode 100644 index 00000000..d9a94a82 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java | |||
@@ -0,0 +1,278 @@ | |||
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.rete.aggregation; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Map; | ||
15 | import java.util.Map.Entry; | ||
16 | |||
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.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
23 | import tools.refinery.viatra.runtime.rete.index.DefaultIndexerListener; | ||
24 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
25 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
26 | import tools.refinery.viatra.runtime.rete.index.StandardIndexer; | ||
27 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
28 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
29 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
31 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
32 | |||
33 | /** | ||
34 | * A special node depending on a projection indexer to aggregate tuple groups with the same projection. Only propagates | ||
35 | * the aggregates of non-empty groups. Use the outer indexers to circumvent. | ||
36 | * <p> | ||
37 | * This node cannot be used in recursive differential dataflow evaluation. | ||
38 | * | ||
39 | * @author Gabor Bergmann | ||
40 | * @since 1.4 | ||
41 | */ | ||
42 | public abstract class IndexerBasedAggregatorNode extends StandardNode implements IAggregatorNode { | ||
43 | |||
44 | ProjectionIndexer projection; | ||
45 | IndexerBasedAggregatorNode me; | ||
46 | int sourceWidth; | ||
47 | Map<Tuple, Object> mainAggregates; | ||
48 | |||
49 | AggregatorOuterIndexer aggregatorOuterIndexer = null; | ||
50 | AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers = null; | ||
51 | |||
52 | /** | ||
53 | * MUST call initializeWith() afterwards! | ||
54 | */ | ||
55 | public IndexerBasedAggregatorNode(ReteContainer reteContainer) { | ||
56 | super(reteContainer); | ||
57 | this.me = this; | ||
58 | mainAggregates = CollectionsFactory.createMap(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public void networkStructureChanged() { | ||
63 | if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
64 | throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!"); | ||
65 | } | ||
66 | super.networkStructureChanged(); | ||
67 | } | ||
68 | |||
69 | /** | ||
70 | * @param projection | ||
71 | * the projection indexer whose tuple groups should be aggregated | ||
72 | */ | ||
73 | public void initializeWith(ProjectionIndexer projection) { | ||
74 | this.projection = projection; | ||
75 | this.sourceWidth = projection.getMask().indices.length; | ||
76 | |||
77 | for (Tuple signature : projection.getSignatures()) { | ||
78 | mainAggregates.put(signature, aggregateGroup(signature, projection.get(signature))); | ||
79 | } | ||
80 | projection.attachListener(new DefaultIndexerListener(this) { | ||
81 | @Override | ||
82 | public void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) { | ||
83 | aggregateUpdate(direction, updateElement, signature, change); | ||
84 | } | ||
85 | }); | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * Aggregates (reduces) a group of tuples. The group can be null. | ||
90 | */ | ||
91 | public abstract Object aggregateGroup(Tuple signature, Collection<Tuple> group); | ||
92 | |||
93 | |||
94 | /** | ||
95 | * Aggregates (reduces) a group of tuples, having access to the previous aggregated value (before the update) and | ||
96 | * the update definition. Defaults to aggregateGroup(). Override to increase performance. | ||
97 | * @since 2.4 | ||
98 | */ | ||
99 | public Object aggregateGroupAfterUpdate(Tuple signature, Collection<Tuple> currentGroup, Object oldAggregate, | ||
100 | Direction direction, Tuple updateElement, boolean change) { | ||
101 | return aggregateGroup(signature, currentGroup); | ||
102 | } | ||
103 | |||
104 | protected Tuple aggregateAndPack(Tuple signature, Collection<Tuple> group) { | ||
105 | return packResult(signature, aggregateGroup(signature, group)); | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public Indexer getAggregatorOuterIndexer() { | ||
110 | if (aggregatorOuterIndexer == null) { | ||
111 | aggregatorOuterIndexer = new AggregatorOuterIndexer(); | ||
112 | this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer); | ||
113 | // reteContainer.connectAndSynchronize(this, aggregatorOuterIndexer); | ||
114 | } | ||
115 | return aggregatorOuterIndexer; | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature) { | ||
120 | if (aggregatorOuterIdentityIndexers == null) | ||
121 | aggregatorOuterIdentityIndexers = new AggregatorOuterIdentityIndexer[sourceWidth + 1]; | ||
122 | if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) { | ||
123 | aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer( | ||
124 | resultPositionInSignature); | ||
125 | this.getCommunicationTracker().registerDependency(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]); | ||
126 | // reteContainer.connectAndSynchronize(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]); | ||
127 | } | ||
128 | return aggregatorOuterIdentityIndexers[resultPositionInSignature]; | ||
129 | } | ||
130 | |||
131 | @Override | ||
132 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
133 | for (final Entry<Tuple, Object> aggregateEntry : mainAggregates.entrySet()) { | ||
134 | collector.add(packResult(aggregateEntry.getKey(), aggregateEntry.getValue())); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | @Override | ||
139 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
140 | // use all zero timestamps because this node cannot be used in recursive groups anyway | ||
141 | for (final Entry<Tuple, Object> aggregateEntry : mainAggregates.entrySet()) { | ||
142 | collector.put(packResult(aggregateEntry.getKey(), aggregateEntry.getValue()), Timestamp.INSERT_AT_ZERO_TIMELINE); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | protected Tuple packResult(Tuple signature, Object result) { | ||
147 | return Tuples.staticArityLeftInheritanceTupleOf(signature, result); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * @since 2.4 | ||
152 | */ | ||
153 | protected void aggregateUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change) { | ||
154 | Collection<Tuple> currentGroup = projection.get(signature); | ||
155 | // these will be null if group is empty | ||
156 | Object oldAggregate = mainAggregates.get(signature); | ||
157 | Object safeOldAggregate = oldAggregate == null ? aggregateGroup(signature, null) : oldAggregate; | ||
158 | boolean empty = currentGroup == null || currentGroup.isEmpty(); | ||
159 | Object newAggregate = empty ? null : aggregateGroupAfterUpdate(signature, currentGroup, safeOldAggregate/* | ||
160 | * non-null | ||
161 | */, | ||
162 | direction, updateElement, change); | ||
163 | if (!empty) | ||
164 | mainAggregates.put(signature, newAggregate); | ||
165 | else | ||
166 | mainAggregates.remove(signature); | ||
167 | Tuple oldTuple = packResult(signature, safeOldAggregate); | ||
168 | Tuple newTuple = packResult(signature, newAggregate == null ? aggregateGroup(signature, null) : newAggregate); | ||
169 | if (oldAggregate != null) | ||
170 | propagateUpdate(Direction.DELETE, oldTuple, Timestamp.ZERO); // direct outputs lack non-empty groups | ||
171 | if (newAggregate != null) | ||
172 | propagateUpdate(Direction.INSERT, newTuple, Timestamp.ZERO); // direct outputs lack non-empty groups | ||
173 | if (aggregatorOuterIndexer != null) | ||
174 | aggregatorOuterIndexer.propagate(signature, oldTuple, newTuple); | ||
175 | if (aggregatorOuterIdentityIndexers != null) | ||
176 | for (AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers) | ||
177 | if (aggregatorOuterIdentityIndexer != null) | ||
178 | aggregatorOuterIdentityIndexer.propagate(signature, oldTuple, newTuple); | ||
179 | } | ||
180 | |||
181 | private Object getAggregate(Tuple signature) { | ||
182 | Object aggregate = mainAggregates.get(signature); | ||
183 | return aggregate == null ? aggregateGroup(signature, null) : aggregate; | ||
184 | } | ||
185 | |||
186 | @Override | ||
187 | public void assignTraceInfo(TraceInfo traceInfo) { | ||
188 | super.assignTraceInfo(traceInfo); | ||
189 | if (traceInfo.propagateToIndexerParent() && projection != null) | ||
190 | projection.acceptPropagatedTraceInfo(traceInfo); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original | ||
195 | * signature. | ||
196 | * | ||
197 | * @author Gabor Bergmann | ||
198 | */ | ||
199 | class AggregatorOuterIndexer extends StandardIndexer { | ||
200 | |||
201 | public AggregatorOuterIndexer() { | ||
202 | super(me.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1)); | ||
203 | this.parent = me; | ||
204 | } | ||
205 | |||
206 | @Override | ||
207 | public Collection<Tuple> get(Tuple signature) { | ||
208 | return Collections.singleton(packResult(signature, getAggregate(signature))); | ||
209 | } | ||
210 | |||
211 | public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) { | ||
212 | propagate(Direction.INSERT, newTuple, signature, false, Timestamp.ZERO); | ||
213 | propagate(Direction.DELETE, oldTuple, signature, false, Timestamp.ZERO); | ||
214 | } | ||
215 | |||
216 | @Override | ||
217 | public Node getActiveNode() { | ||
218 | return projection.getActiveNode(); | ||
219 | } | ||
220 | |||
221 | } | ||
222 | |||
223 | /** | ||
224 | * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for | ||
225 | * this index is the original signature of the projection index, with the suspected result inserted at position | ||
226 | * resultPositionInSignature. | ||
227 | * | ||
228 | * @author Gabor Bergmann | ||
229 | */ | ||
230 | |||
231 | class AggregatorOuterIdentityIndexer extends StandardIndexer { | ||
232 | int resultPositionInSignature; | ||
233 | TupleMask pruneResult; | ||
234 | TupleMask reorderMask; | ||
235 | |||
236 | public AggregatorOuterIdentityIndexer(int resultPositionInSignature) { | ||
237 | super(me.reteContainer, TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1)); | ||
238 | this.parent = me; | ||
239 | // this.localAggregates = new HashMap<Tuple, Tuple>(); | ||
240 | this.resultPositionInSignature = resultPositionInSignature; | ||
241 | this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1); | ||
242 | if (resultPositionInSignature == sourceWidth) | ||
243 | this.reorderMask = null; | ||
244 | else | ||
245 | this.reorderMask = mask; | ||
246 | } | ||
247 | |||
248 | @Override | ||
249 | public Collection<Tuple> get(Tuple signatureWithResult) { | ||
250 | Tuple prunedSignature = pruneResult.transform(signatureWithResult); | ||
251 | Object result = getAggregate(prunedSignature); | ||
252 | if (signatureWithResult.get(resultPositionInSignature).equals(result)) | ||
253 | return Collections.singleton(signatureWithResult); | ||
254 | else | ||
255 | return null; | ||
256 | } | ||
257 | |||
258 | public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) { | ||
259 | propagate(Direction.INSERT, reorder(newTuple), signature, true, Timestamp.ZERO); | ||
260 | propagate(Direction.DELETE, reorder(oldTuple), signature, true, Timestamp.ZERO); | ||
261 | } | ||
262 | |||
263 | private Tuple reorder(Tuple signatureWithResult) { | ||
264 | Tuple transformed; | ||
265 | if (reorderMask == null) | ||
266 | transformed = signatureWithResult; | ||
267 | else | ||
268 | transformed = reorderMask.transform(signatureWithResult); | ||
269 | return transformed; | ||
270 | } | ||
271 | |||
272 | @Override | ||
273 | public Node getActiveNode() { | ||
274 | return projection.getActiveNode(); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..19e02f10 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java | |||
@@ -0,0 +1,212 @@ | |||
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.rete.aggregation.timely; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.*; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
16 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.CumulativeAggregate; | ||
17 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.FoldingState; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; | ||
21 | |||
22 | import java.util.Collections; | ||
23 | import java.util.Map; | ||
24 | import java.util.Map.Entry; | ||
25 | import java.util.Objects; | ||
26 | import java.util.TreeMap; | ||
27 | |||
28 | /** | ||
29 | * Faithful column aggregator with parallel aggregation architecture. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * @since 2.4 | ||
33 | * | ||
34 | */ | ||
35 | public class FaithfulParallelTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends | ||
36 | FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate<Domain, Accumulator>, FoldingState<Domain>> | ||
37 | implements ResumableNode { | ||
38 | |||
39 | public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer, | ||
40 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
41 | final TupleMask groupMask, final TupleMask columnMask) { | ||
42 | super(reteContainer, operator, groupMask, columnMask); | ||
43 | } | ||
44 | |||
45 | public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer, | ||
46 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
47 | final TupleMask groupMask, final int aggregatedColumn) { | ||
48 | this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth)); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | protected Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group, final FoldingState<Domain> state, | ||
53 | final Timestamp timestamp) { | ||
54 | final CumulativeAggregate<Domain, Accumulator> aggregate = getAggregate(group, timestamp); | ||
55 | if (state.delta.isEmpty()) { | ||
56 | gcAggregates(aggregate, group, timestamp); | ||
57 | return Collections.emptyMap(); | ||
58 | } else { | ||
59 | final Map<AggregateResult, Diff<Timestamp>> diffMap = CollectionsFactory.createMap(); | ||
60 | final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp); | ||
61 | |||
62 | final AggregateResult currentOldResult = operator.getAggregate(aggregate.accumulator); | ||
63 | |||
64 | for (final Entry<Domain, Integer> entry : state.delta.entriesWithMultiplicities()) { | ||
65 | final boolean isInsertion = entry.getValue() > 0; | ||
66 | final Domain aggregand = entry.getKey(); | ||
67 | for (int i = 0; i < Math.abs(entry.getValue()); i++) { | ||
68 | aggregate.accumulator = operator.update(aggregate.accumulator, aggregand, isInsertion); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | final AggregateResult currentNewResult = operator.getAggregate(aggregate.accumulator); | ||
73 | |||
74 | if (!Objects.equals(currentOldResult, currentNewResult)) { | ||
75 | // current old result disappears here | ||
76 | appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap); | ||
77 | if (nextTimestamp != null) { | ||
78 | appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap); | ||
79 | } | ||
80 | |||
81 | // current new result appears here | ||
82 | appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap); | ||
83 | if (nextTimestamp != null) { | ||
84 | appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | gcAggregates(aggregate, group, timestamp); | ||
89 | updateTimeline(group, diffMap); | ||
90 | |||
91 | // prepare folding state for next timestamp | ||
92 | if (nextTimestamp != null) { | ||
93 | final FoldingState<Domain> newState = new FoldingState<>(); | ||
94 | newState.delta = state.delta; | ||
95 | addFoldingState(group, newState, nextTimestamp); | ||
96 | } | ||
97 | |||
98 | return diffMap; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
104 | final Tuple group = groupMask.transform(update); | ||
105 | final Tuple value = columnMask.transform(update); | ||
106 | @SuppressWarnings("unchecked") | ||
107 | final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); | ||
108 | final boolean isInsertion = direction == Direction.INSERT; | ||
109 | |||
110 | final CumulativeAggregate<Domain, Accumulator> aggregate = getAggregate(group, timestamp); | ||
111 | final FoldingState<Domain> state = new FoldingState<>(); | ||
112 | if (isInsertion) { | ||
113 | aggregate.aggregands.addOne(aggregand); | ||
114 | state.delta.addOne(aggregand); | ||
115 | } else { | ||
116 | aggregate.aggregands.removeOne(aggregand); | ||
117 | state.delta.removeOne(aggregand); | ||
118 | } | ||
119 | |||
120 | addFoldingState(group, state, timestamp); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty. | ||
125 | */ | ||
126 | @Override | ||
127 | protected void gcAggregates(final CumulativeAggregate<Domain, Accumulator> aggregate, final Tuple group, | ||
128 | final Timestamp timestamp) { | ||
129 | if (aggregate.aggregands.isEmpty()) { | ||
130 | final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates | ||
131 | .get(group); | ||
132 | groupAggregates.remove(timestamp); | ||
133 | if (groupAggregates.isEmpty()) { | ||
134 | this.aggregates.remove(group); | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * On-demand initializes and returns the aggregate for the given group and timestamp. | ||
141 | */ | ||
142 | @Override | ||
143 | protected CumulativeAggregate<Domain, Accumulator> getAggregate(final Tuple group, final Timestamp timestamp) { | ||
144 | final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates | ||
145 | .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap()); | ||
146 | return groupAggregates.computeIfAbsent(timestamp, k -> { | ||
147 | final CumulativeAggregate<Domain, Accumulator> aggregate = new CumulativeAggregate<>(); | ||
148 | final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator>> lowerEntry = groupAggregates | ||
149 | .lowerEntry(timestamp); | ||
150 | if (lowerEntry == null) { | ||
151 | aggregate.accumulator = operator.createNeutral(); | ||
152 | } else { | ||
153 | aggregate.accumulator = operator.clone(lowerEntry.getValue().accumulator); | ||
154 | } | ||
155 | return aggregate; | ||
156 | }); | ||
157 | } | ||
158 | |||
159 | @Override | ||
160 | public AggregateResult getAggregateResult(final Tuple group) { | ||
161 | final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator>> groupAggregates = this.aggregates.get(group); | ||
162 | if (groupAggregates != null) { | ||
163 | final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator>> lastEntry = groupAggregates.lastEntry(); | ||
164 | return operator.getAggregate(lastEntry.getValue().accumulator); | ||
165 | } else { | ||
166 | return NEUTRAL; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | protected static class CumulativeAggregate<Domain, Accumulator> { | ||
171 | protected Accumulator accumulator; | ||
172 | protected IDeltaBag<Domain> aggregands; | ||
173 | |||
174 | protected CumulativeAggregate() { | ||
175 | this.aggregands = CollectionsFactory.createDeltaBag(); | ||
176 | } | ||
177 | |||
178 | @Override | ||
179 | public String toString() { | ||
180 | return "accumulator=" + accumulator + " aggregands=" + aggregands; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | protected static class FoldingState<Domain> implements MergeableFoldingState<FoldingState<Domain>> { | ||
185 | protected IDeltaBag<Domain> delta; | ||
186 | |||
187 | protected FoldingState() { | ||
188 | this.delta = CollectionsFactory.createDeltaBag(); | ||
189 | } | ||
190 | |||
191 | @Override | ||
192 | public String toString() { | ||
193 | return "delta=" + delta; | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * The returned result will never be null, even if the resulting delta set is empty. | ||
198 | */ | ||
199 | @Override | ||
200 | public FoldingState<Domain> merge(final FoldingState<Domain> that) { | ||
201 | Preconditions.checkArgument(that != null); | ||
202 | // 'this' was the previously registered folding state | ||
203 | // 'that' is the new folding state being pushed upwards | ||
204 | final FoldingState<Domain> result = new FoldingState<>(); | ||
205 | this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); | ||
206 | that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); | ||
207 | return result; | ||
208 | } | ||
209 | |||
210 | } | ||
211 | |||
212 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..cf2c2b2d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java | |||
@@ -0,0 +1,279 @@ | |||
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.rete.aggregation.timely; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Objects; | ||
15 | import java.util.TreeMap; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
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.Direction; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.IDeltaBag; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
26 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.CumulativeAggregate; | ||
27 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.FoldingState; | ||
28 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
29 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; | ||
31 | |||
32 | /** | ||
33 | * Faithful column aggregator with sequential aggregation architecture. | ||
34 | * | ||
35 | * @author Tamas Szabo | ||
36 | * @since 2.4 | ||
37 | * | ||
38 | */ | ||
39 | public class FaithfulSequentialTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> extends | ||
40 | FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate<Domain, Accumulator, AggregateResult>, FoldingState<Domain, AggregateResult>> | ||
41 | implements ResumableNode { | ||
42 | |||
43 | protected boolean isRecursiveAggregation; | ||
44 | |||
45 | public FaithfulSequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer, | ||
46 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
47 | final TupleMask groupMask, final TupleMask columnMask) { | ||
48 | super(reteContainer, operator, groupMask, columnMask); | ||
49 | this.isRecursiveAggregation = false; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void networkStructureChanged() { | ||
54 | super.networkStructureChanged(); | ||
55 | this.isRecursiveAggregation = this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | protected Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group, | ||
60 | final FoldingState<Domain, AggregateResult> state, final Timestamp timestamp) { | ||
61 | final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = getAggregate(group, timestamp); | ||
62 | if (state.delta.isEmpty() && Objects.equals(state.oldResult, state.newResult)) { | ||
63 | gcAggregates(aggregate, group, timestamp); | ||
64 | return Collections.emptyMap(); | ||
65 | } else { | ||
66 | final Map<AggregateResult, Diff<Timestamp>> diffMap = CollectionsFactory.createMap(); | ||
67 | final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp); | ||
68 | |||
69 | final AggregateResult previousOldResult = state.oldResult; | ||
70 | final AggregateResult previousNewResult = state.newResult; | ||
71 | |||
72 | final AggregateResult currentOldResult = previousOldResult == null | ||
73 | ? operator.getAggregate(aggregate.positive) | ||
74 | : operator.combine(previousOldResult, aggregate.positive); | ||
75 | |||
76 | for (final Entry<Domain, Integer> entry : state.delta.entriesWithMultiplicities()) { | ||
77 | final boolean isInsertion = entry.getValue() > 0; | ||
78 | final Domain aggregand = entry.getKey(); | ||
79 | if (isInsertion) { | ||
80 | for (int i = 0; i < entry.getValue(); i++) { | ||
81 | if (isRecursiveAggregation) { | ||
82 | final boolean contains = aggregate.negative.containsNonZero(aggregand); | ||
83 | if (contains) { | ||
84 | aggregate.negative.addOne(aggregand); | ||
85 | } else { | ||
86 | aggregate.positive = operator.update(aggregate.positive, aggregand, true); | ||
87 | } | ||
88 | } else { | ||
89 | aggregate.positive = operator.update(aggregate.positive, aggregand, true); | ||
90 | } | ||
91 | } | ||
92 | } else { | ||
93 | for (int i = 0; i < -entry.getValue(); i++) { | ||
94 | if (isRecursiveAggregation) { | ||
95 | final boolean contains = operator.contains(aggregand, aggregate.positive); | ||
96 | if (contains) { | ||
97 | aggregate.positive = operator.update(aggregate.positive, aggregand, false); | ||
98 | } else { | ||
99 | aggregate.negative.removeOne(aggregand); | ||
100 | } | ||
101 | } else { | ||
102 | aggregate.positive = operator.update(aggregate.positive, aggregand, false); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | final AggregateResult currentNewResult = previousNewResult == null | ||
109 | ? operator.getAggregate(aggregate.positive) | ||
110 | : operator.combine(previousNewResult, aggregate.positive); | ||
111 | |||
112 | aggregate.cachedResult = currentNewResult; | ||
113 | |||
114 | final boolean sameResult = Objects.equals(currentOldResult, currentNewResult); | ||
115 | if (!sameResult) { | ||
116 | // current old result disappears here | ||
117 | appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap); | ||
118 | if (nextTimestamp != null) { | ||
119 | appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap); | ||
120 | } | ||
121 | |||
122 | // current new result appears here | ||
123 | appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap); | ||
124 | if (nextTimestamp != null) { | ||
125 | appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | gcAggregates(aggregate, group, timestamp); | ||
130 | updateTimeline(group, diffMap); | ||
131 | |||
132 | // prepare folding state for next timestamp | ||
133 | if (nextTimestamp != null && !sameResult) { | ||
134 | final FoldingState<Domain, AggregateResult> newState = new FoldingState<>(); | ||
135 | // DO NOT push forward the delta in the folding state!!! that one only affects the input timestamp | ||
136 | newState.oldResult = currentOldResult; | ||
137 | newState.newResult = currentNewResult; | ||
138 | addFoldingState(group, newState, nextTimestamp); | ||
139 | } | ||
140 | |||
141 | return diffMap; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
147 | final Tuple group = groupMask.transform(update); | ||
148 | final Tuple value = columnMask.transform(update); | ||
149 | @SuppressWarnings("unchecked") | ||
150 | final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); | ||
151 | final boolean isInsertion = direction == Direction.INSERT; | ||
152 | |||
153 | final AggregateResult previousResult = getResultRaw(group, timestamp, true); | ||
154 | final FoldingState<Domain, AggregateResult> state = new FoldingState<Domain, AggregateResult>(); | ||
155 | if (isInsertion) { | ||
156 | state.delta.addOne(aggregand); | ||
157 | } else { | ||
158 | state.delta.removeOne(aggregand); | ||
159 | } | ||
160 | state.oldResult = previousResult; | ||
161 | state.newResult = previousResult; | ||
162 | |||
163 | // it is acceptable if both oldResult and newResult are null at this point | ||
164 | // in that case we did not have a previous entry at a lower timestamp | ||
165 | |||
166 | addFoldingState(group, state, timestamp); | ||
167 | } | ||
168 | |||
169 | protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) { | ||
170 | final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> entryMap = this.aggregates | ||
171 | .get(group); | ||
172 | if (entryMap == null) { | ||
173 | return null; | ||
174 | } else { | ||
175 | CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = null; | ||
176 | if (lower) { | ||
177 | final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> lowerEntry = entryMap | ||
178 | .lowerEntry(timestamp); | ||
179 | if (lowerEntry != null) { | ||
180 | aggregate = lowerEntry.getValue(); | ||
181 | } | ||
182 | } else { | ||
183 | aggregate = entryMap.get(timestamp); | ||
184 | } | ||
185 | if (aggregate == null) { | ||
186 | return null; | ||
187 | } else { | ||
188 | return aggregate.cachedResult; | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | |||
193 | @Override | ||
194 | protected void gcAggregates(final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate, | ||
195 | final Tuple group, final Timestamp timestamp) { | ||
196 | if (operator.isNeutral(aggregate.positive) && aggregate.negative.isEmpty()) { | ||
197 | final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates | ||
198 | .get(group); | ||
199 | groupAggregates.remove(timestamp); | ||
200 | if (groupAggregates.isEmpty()) { | ||
201 | this.aggregates.remove(group); | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | @Override | ||
207 | protected CumulativeAggregate<Domain, Accumulator, AggregateResult> getAggregate(final Tuple group, | ||
208 | final Timestamp timestamp) { | ||
209 | final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates | ||
210 | .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap()); | ||
211 | return groupAggregates.computeIfAbsent(timestamp, k -> { | ||
212 | final CumulativeAggregate<Domain, Accumulator, AggregateResult> aggregate = new CumulativeAggregate<>(); | ||
213 | aggregate.positive = operator.createNeutral(); | ||
214 | return aggregate; | ||
215 | }); | ||
216 | } | ||
217 | |||
218 | @Override | ||
219 | public AggregateResult getAggregateResult(final Tuple group) { | ||
220 | final TreeMap<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> groupAggregates = this.aggregates | ||
221 | .get(group); | ||
222 | if (groupAggregates != null) { | ||
223 | final Entry<Timestamp, CumulativeAggregate<Domain, Accumulator, AggregateResult>> lastEntry = groupAggregates | ||
224 | .lastEntry(); | ||
225 | return lastEntry.getValue().cachedResult; | ||
226 | } else { | ||
227 | return NEUTRAL; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | protected static class CumulativeAggregate<Domain, Accumulator, AggregateResult> { | ||
232 | protected Accumulator positive; | ||
233 | protected IDeltaBag<Domain> negative; | ||
234 | protected AggregateResult cachedResult; | ||
235 | |||
236 | protected CumulativeAggregate() { | ||
237 | this.negative = CollectionsFactory.createDeltaBag(); | ||
238 | } | ||
239 | |||
240 | @Override | ||
241 | public String toString() { | ||
242 | return "positive=" + positive + " negative=" + negative + " cachedResult=" + cachedResult; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | protected static class FoldingState<Domain, AggregateResult> | ||
247 | implements MergeableFoldingState<FoldingState<Domain, AggregateResult>> { | ||
248 | protected IDeltaBag<Domain> delta; | ||
249 | protected AggregateResult oldResult; | ||
250 | protected AggregateResult newResult; | ||
251 | |||
252 | protected FoldingState() { | ||
253 | this.delta = CollectionsFactory.createDeltaBag(); | ||
254 | } | ||
255 | |||
256 | @Override | ||
257 | public String toString() { | ||
258 | return "delta=" + delta + " oldResult=" + oldResult + " newResult=" + newResult; | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * The returned result will never be null, even if the resulting delta set is empty. | ||
263 | */ | ||
264 | @Override | ||
265 | public FoldingState<Domain, AggregateResult> merge(final FoldingState<Domain, AggregateResult> that) { | ||
266 | Preconditions.checkArgument(that != null); | ||
267 | // 'this' was the previously registered folding state | ||
268 | // 'that' is the new folding state being pushed upwards | ||
269 | final FoldingState<Domain, AggregateResult> result = new FoldingState<Domain, AggregateResult>(); | ||
270 | this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); | ||
271 | that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); | ||
272 | result.oldResult = this.oldResult; | ||
273 | result.newResult = that.newResult; | ||
274 | return result; | ||
275 | } | ||
276 | |||
277 | } | ||
278 | |||
279 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..8fe9a4e9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java | |||
@@ -0,0 +1,247 @@ | |||
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.rete.aggregation.timely; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Objects; | ||
15 | import java.util.TreeMap; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
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.Signed; | ||
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 | import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode; | ||
26 | import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap; | ||
27 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState; | ||
28 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
29 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
31 | import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; | ||
32 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
33 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
34 | |||
35 | /** | ||
36 | * Faithful timely implementation of the column aggregator node. Complete timelines (series of appearance & | ||
37 | * disappearance) are maintained for tuples. <br> | ||
38 | * <br> | ||
39 | * Subclasses are responsible for implementing the aggregator architecture, and they must use the CumulativeAggregate | ||
40 | * type parameter for that. <br> | ||
41 | * <br> | ||
42 | * This node supports recursive aggregation. | ||
43 | * | ||
44 | * @author Tamas Szabo | ||
45 | * @since 2.4 | ||
46 | */ | ||
47 | public abstract class FaithfulTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult, CumulativeAggregate, FoldingState extends MergeableFoldingState<FoldingState>> | ||
48 | extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> implements ResumableNode { | ||
49 | |||
50 | protected final Map<Tuple, TreeMap<Timestamp, CumulativeAggregate>> aggregates; | ||
51 | protected final Map<Tuple, Map<AggregateResult, Timeline<Timestamp>>> timelines; | ||
52 | protected final TreeMap<Timestamp, Map<Tuple, FoldingState>> foldingState; | ||
53 | protected CommunicationGroup communicationGroup; | ||
54 | |||
55 | public FaithfulTimelyColumnAggregatorNode(final ReteContainer reteContainer, | ||
56 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
57 | final TupleMask groupMask, final TupleMask columnMask) { | ||
58 | super(reteContainer, operator, groupMask, columnMask); | ||
59 | this.aggregates = CollectionsFactory.createMap(); | ||
60 | this.timelines = CollectionsFactory.createMap(); | ||
61 | this.foldingState = CollectionsFactory.createTreeMap(); | ||
62 | // mailbox MUST be instantiated after the fields are all set | ||
63 | this.mailbox = instantiateMailbox(); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | protected Mailbox instantiateMailbox() { | ||
68 | return new TimelyMailbox(this, this.reteContainer); | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public void clear() { | ||
73 | this.mailbox.clear(); | ||
74 | this.aggregates.clear(); | ||
75 | this.timelines.clear(); | ||
76 | this.children.clear(); | ||
77 | this.childMailboxes.clear(); | ||
78 | this.foldingState.clear(); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the | ||
83 | * two states will be merged together. | ||
84 | * | ||
85 | * | ||
86 | */ | ||
87 | protected void addFoldingState(final Tuple group, final FoldingState state, final Timestamp timestamp) { | ||
88 | // assert !state.delta.isEmpty(); | ||
89 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.computeIfAbsent(timestamp, | ||
90 | k -> CollectionsFactory.createMap()); | ||
91 | tupleMap.compute(group, (k, v) -> { | ||
92 | return v == null ? state : v.merge(state); | ||
93 | }); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Timestamp getResumableTimestamp() { | ||
98 | if (this.foldingState.isEmpty()) { | ||
99 | return null; | ||
100 | } else { | ||
101 | return this.foldingState.firstKey(); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public void resumeAt(final Timestamp timestamp) { | ||
107 | Timestamp current = this.getResumableTimestamp(); | ||
108 | if (current == null) { | ||
109 | throw new IllegalStateException("There is nothing to fold!"); | ||
110 | } else if (current.compareTo(timestamp) != 0) { | ||
111 | throw new IllegalStateException("Expected to continue folding at " + timestamp + "!"); | ||
112 | } | ||
113 | |||
114 | final Map<Tuple, FoldingState> tupleMap = this.foldingState.remove(timestamp); | ||
115 | for (final Entry<Tuple, FoldingState> groupEntry : tupleMap.entrySet()) { | ||
116 | final Tuple group = groupEntry.getKey(); | ||
117 | final FoldingState value = groupEntry.getValue(); | ||
118 | final Map<AggregateResult, Diff<Timestamp>> diffMap = doFoldingStep(group, value, timestamp); | ||
119 | for (final Entry<AggregateResult, Diff<Timestamp>> resultEntry : diffMap.entrySet()) { | ||
120 | for (final Signed<Timestamp> signed : resultEntry.getValue()) { | ||
121 | propagate(signed.getDirection(), group, resultEntry.getKey(), signed.getPayload()); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | final Timestamp nextTimestamp = this.getResumableTimestamp(); | ||
127 | if (Objects.equals(timestamp, nextTimestamp)) { | ||
128 | throw new IllegalStateException( | ||
129 | "Folding at " + timestamp + " produced more folding work at the same timestamp!"); | ||
130 | } else if (nextTimestamp != null) { | ||
131 | this.communicationGroup.notifyHasMessage(this.mailbox, nextTimestamp); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | protected abstract Map<AggregateResult, Diff<Timestamp>> doFoldingStep(final Tuple group, final FoldingState state, | ||
136 | final Timestamp timestamp); | ||
137 | |||
138 | /** | ||
139 | * Updates and garbage collects the timeline of the given tuple based on the given diffs. | ||
140 | */ | ||
141 | protected void updateTimeline(final Tuple group, final Map<AggregateResult, Diff<Timestamp>> diffs) { | ||
142 | if (!diffs.isEmpty()) { | ||
143 | this.timelines.compute(group, (k, resultTimelines) -> { | ||
144 | if (resultTimelines == null) { | ||
145 | resultTimelines = CollectionsFactory.createMap(); | ||
146 | } | ||
147 | for (final Entry<AggregateResult, Diff<Timestamp>> entry : diffs.entrySet()) { | ||
148 | final AggregateResult result = entry.getKey(); | ||
149 | resultTimelines.compute(result, (k2, oldResultTimeline) -> { | ||
150 | final Diff<Timestamp> currentResultDiffs = entry.getValue(); | ||
151 | if (oldResultTimeline == null) { | ||
152 | oldResultTimeline = getInitialTimeline(result); | ||
153 | } | ||
154 | final Timeline<Timestamp> timeline = oldResultTimeline.mergeAdditive(currentResultDiffs); | ||
155 | if (timeline.isEmpty()) { | ||
156 | return null; | ||
157 | } else { | ||
158 | return timeline; | ||
159 | } | ||
160 | }); | ||
161 | } | ||
162 | if (resultTimelines.isEmpty()) { | ||
163 | return null; | ||
164 | } else { | ||
165 | return resultTimelines; | ||
166 | } | ||
167 | }); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty. | ||
173 | */ | ||
174 | protected abstract void gcAggregates(final CumulativeAggregate aggregate, final Tuple group, | ||
175 | final Timestamp timestamp); | ||
176 | |||
177 | /** | ||
178 | * On-demand initializes and returns the aggregate for the given group and timestamp. | ||
179 | */ | ||
180 | protected abstract CumulativeAggregate getAggregate(final Tuple group, final Timestamp timestamp); | ||
181 | |||
182 | protected static final Timeline<Timestamp> NEUTRAL_INITIAL_TIMELINE = Timestamp.INSERT_AT_ZERO_TIMELINE; | ||
183 | protected static final Timeline<Timestamp> NON_NEUTRAL_INITIAL_TIMELINE = Timelines.createEmpty(); | ||
184 | |||
185 | protected Timeline<Timestamp> getInitialTimeline(final AggregateResult result) { | ||
186 | if (NEUTRAL == result) { | ||
187 | return NEUTRAL_INITIAL_TIMELINE; | ||
188 | } else { | ||
189 | return NON_NEUTRAL_INITIAL_TIMELINE; | ||
190 | } | ||
191 | } | ||
192 | |||
193 | protected static <AggregateResult> void appendDiff(final AggregateResult result, final Signed<Timestamp> diff, | ||
194 | final Map<AggregateResult, Diff<Timestamp>> diffs) { | ||
195 | if (result != null) { | ||
196 | diffs.compute(result, (k, timeLineDiff) -> { | ||
197 | if (timeLineDiff == null) { | ||
198 | timeLineDiff = new Diff<>(); | ||
199 | } | ||
200 | timeLineDiff.add(diff); | ||
201 | return timeLineDiff; | ||
202 | }); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | @Override | ||
207 | public Tuple getAggregateTuple(final Tuple group) { | ||
208 | return tupleFromAggregateResult(group, getAggregateResult(group)); | ||
209 | } | ||
210 | |||
211 | @Override | ||
212 | public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple group) { | ||
213 | final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = this.timelines.get(group); | ||
214 | if (resultTimelines == null) { | ||
215 | if (NEUTRAL == null) { | ||
216 | return Collections.emptyMap(); | ||
217 | } else { | ||
218 | return Collections.singletonMap(NEUTRAL, NEUTRAL_INITIAL_TIMELINE); | ||
219 | } | ||
220 | } else { | ||
221 | return resultTimelines; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | @Override | ||
226 | public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple group) { | ||
227 | final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = getAggregateResultTimeline(group); | ||
228 | return new GroupedMap<AggregateResult, Timeline<Timestamp>>(group, resultTimelines, this.runtimeContext); | ||
229 | } | ||
230 | |||
231 | @Override | ||
232 | public CommunicationGroup getCurrentGroup() { | ||
233 | return communicationGroup; | ||
234 | } | ||
235 | |||
236 | @Override | ||
237 | public void setCurrentGroup(final CommunicationGroup currentGroup) { | ||
238 | this.communicationGroup = currentGroup; | ||
239 | } | ||
240 | |||
241 | protected interface MergeableFoldingState<T> { | ||
242 | |||
243 | public abstract T merge(final T that); | ||
244 | |||
245 | } | ||
246 | |||
247 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..733d2585 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java | |||
@@ -0,0 +1,106 @@ | |||
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.rete.aggregation.timely; | ||
10 | |||
11 | import java.util.Map.Entry; | ||
12 | import java.util.TreeMap; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
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.Direction; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | |||
21 | /** | ||
22 | * First-only column aggregator with parallel aggregation architecture. | ||
23 | * | ||
24 | * @author Tamas Szabo | ||
25 | * @since 2.4 | ||
26 | */ | ||
27 | public class FirstOnlyParallelTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> | ||
28 | extends FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> { | ||
29 | |||
30 | public FirstOnlyParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer, | ||
31 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
32 | final TupleMask groupMask, final TupleMask columnMask) { | ||
33 | super(reteContainer, operator, groupMask, columnMask); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Accumulator gets modified at the input timestamp and at all higher timestamps. Folding cannot be interrupted if | ||
38 | * the new aggregate result is the same as the old at an intermediate timestamp because aggregands need to be copied | ||
39 | * over to all accumulators at the higher timestamps. | ||
40 | */ | ||
41 | @Override | ||
42 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
43 | final Tuple group = groupMask.transform(update); | ||
44 | final Tuple value = columnMask.transform(update); | ||
45 | @SuppressWarnings("unchecked") | ||
46 | final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); | ||
47 | final boolean isInsertion = direction == Direction.INSERT; | ||
48 | |||
49 | final AggregateResult previousResult = getResultRaw(group, timestamp, true); | ||
50 | |||
51 | Accumulator oldAccumulator = getAccumulator(group, timestamp); | ||
52 | AggregateResult oldResult = operator.getAggregate(oldAccumulator); | ||
53 | |||
54 | Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion); | ||
55 | AggregateResult newResult = operator.getAggregate(newAccumulator); | ||
56 | |||
57 | storeIfNotNeutral(group, newAccumulator, newResult, timestamp); | ||
58 | |||
59 | propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult); | ||
60 | |||
61 | AggregateResult previousOldResult = oldResult; | ||
62 | AggregateResult previousNewResult = newResult; | ||
63 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupEntries = this.memory | ||
64 | .get(group); | ||
65 | |||
66 | Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp); | ||
67 | |||
68 | while (currentTimestamp != null) { | ||
69 | final CumulativeAggregate<Accumulator, AggregateResult> groupEntry = groupEntries.get(currentTimestamp); | ||
70 | oldResult = groupEntry.result; | ||
71 | oldAccumulator = groupEntry.accumulator; | ||
72 | newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion); | ||
73 | newResult = operator.getAggregate(newAccumulator); | ||
74 | |||
75 | storeIfNotNeutral(group, newAccumulator, newResult, currentTimestamp); | ||
76 | |||
77 | propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, oldResult, newResult); | ||
78 | |||
79 | previousOldResult = oldResult; | ||
80 | previousNewResult = newResult; | ||
81 | currentTimestamp = groupEntries.higherKey(currentTimestamp); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) { | ||
87 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group); | ||
88 | if (entryMap == null) { | ||
89 | return operator.createNeutral(); | ||
90 | } else { | ||
91 | final CumulativeAggregate<Accumulator, AggregateResult> entry = entryMap.get(timestamp); | ||
92 | if (entry == null) { | ||
93 | final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> lowerEntry = entryMap | ||
94 | .lowerEntry(timestamp); | ||
95 | if (lowerEntry == null) { | ||
96 | return operator.createNeutral(); | ||
97 | } else { | ||
98 | return operator.clone(lowerEntry.getValue().accumulator); | ||
99 | } | ||
100 | } else { | ||
101 | return entry.accumulator; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..79197aac --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java | |||
@@ -0,0 +1,117 @@ | |||
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.rete.aggregation.timely; | ||
10 | |||
11 | import java.util.Objects; | ||
12 | import java.util.TreeMap; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
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.Direction; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | |||
21 | /** | ||
22 | * First-only column aggregator with sequential aggregation architecture. | ||
23 | * | ||
24 | * @author Tamas Szabo | ||
25 | * @since 2.4 | ||
26 | */ | ||
27 | public class FirstOnlySequentialTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> | ||
28 | extends FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> { | ||
29 | |||
30 | public FirstOnlySequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer, | ||
31 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
32 | final TupleMask groupMask, final TupleMask columnMask) { | ||
33 | super(reteContainer, operator, groupMask, columnMask); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Accumulator gets modified only at the timestamp where the update happened. During the folding, accumulators are | ||
38 | * never changed at higher timestamps. Aggregate results at higher timestamps may change due to the change at the | ||
39 | * input timestamp. Uniqueness enforcement may require from aggregate results to jump up/down on demand during the | ||
40 | * folding. | ||
41 | */ | ||
42 | @Override | ||
43 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
44 | final Tuple group = groupMask.transform(update); | ||
45 | final Tuple value = columnMask.transform(update); | ||
46 | @SuppressWarnings("unchecked") | ||
47 | final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); | ||
48 | final boolean isInsertion = direction == Direction.INSERT; | ||
49 | |||
50 | final AggregateResult previousResult = getResultRaw(group, timestamp, true); | ||
51 | |||
52 | final Accumulator oldAccumulator = getAccumulator(group, timestamp); | ||
53 | final AggregateResult oldResult = previousResult == null ? operator.getAggregate(oldAccumulator) | ||
54 | : operator.combine(previousResult, oldAccumulator); | ||
55 | |||
56 | final Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion); | ||
57 | final AggregateResult newResult = previousResult == null ? operator.getAggregate(newAccumulator) | ||
58 | : operator.combine(previousResult, newAccumulator); | ||
59 | |||
60 | storeIfNotNeutral(group, newAccumulator, newResult, timestamp); | ||
61 | |||
62 | propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult); | ||
63 | |||
64 | // fold up the state towards higher timestamps | ||
65 | if (!Objects.equals(oldResult, newResult)) { | ||
66 | AggregateResult previousOldResult = oldResult; | ||
67 | AggregateResult previousNewResult = newResult; | ||
68 | AggregateResult currentOldResult = null; | ||
69 | AggregateResult currentNewResult = null; | ||
70 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupEntries = this.memory | ||
71 | .get(group); | ||
72 | |||
73 | Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp); | ||
74 | |||
75 | while (currentTimestamp != null) { | ||
76 | // they cannot be the same, otherwise we would not even be here | ||
77 | assert !Objects.equals(previousOldResult, previousNewResult); | ||
78 | |||
79 | final Accumulator accumulator = getAccumulator(group, currentTimestamp); | ||
80 | currentOldResult = groupEntries.get(currentTimestamp).result; | ||
81 | currentNewResult = operator.combine(previousNewResult, accumulator); | ||
82 | |||
83 | // otherwise we would not be iterating over this timestamp | ||
84 | assert !operator.isNeutral(accumulator); | ||
85 | |||
86 | propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, currentOldResult, | ||
87 | currentNewResult); | ||
88 | |||
89 | if (!Objects.equals(currentOldResult, currentNewResult)) { | ||
90 | storeIfNotNeutral(group, accumulator, currentNewResult, currentTimestamp); | ||
91 | previousOldResult = currentOldResult; | ||
92 | previousNewResult = currentNewResult; | ||
93 | currentTimestamp = groupEntries.higherKey(currentTimestamp); | ||
94 | } else { | ||
95 | // we can stop the folding from here | ||
96 | break; | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) { | ||
104 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group); | ||
105 | if (entryMap == null) { | ||
106 | return operator.createNeutral(); | ||
107 | } else { | ||
108 | final CumulativeAggregate<Accumulator, AggregateResult> entry = entryMap.get(timestamp); | ||
109 | if (entry == null) { | ||
110 | return operator.createNeutral(); | ||
111 | } else { | ||
112 | return entry.accumulator; | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..0c73000e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java | |||
@@ -0,0 +1,212 @@ | |||
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.rete.aggregation.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.Objects; | ||
16 | import java.util.TreeMap; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; | ||
25 | import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode; | ||
26 | import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap; | ||
27 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
28 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
29 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
30 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
31 | |||
32 | /** | ||
33 | * First-only timely implementation of the column aggregator node. Only timestamps of appearance are maintained for | ||
34 | * tuples instead of complete timelines. | ||
35 | * <br><br> | ||
36 | * Subclasses are responsible for implementing the aggregator architecture, and they must make use of the inner class {@link CumulativeAggregate}. | ||
37 | * <br><br> | ||
38 | * This node supports recursive aggregation. | ||
39 | * | ||
40 | * @author Tamas Szabo | ||
41 | * @since 2.4 | ||
42 | */ | ||
43 | public abstract class FirstOnlyTimelyColumnAggregatorNode<Domain, Accumulator, AggregateResult> | ||
44 | extends AbstractColumnAggregatorNode<Domain, Accumulator, AggregateResult> { | ||
45 | |||
46 | protected final Map<Tuple, TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>>> memory; | ||
47 | |||
48 | public FirstOnlyTimelyColumnAggregatorNode(final ReteContainer reteContainer, | ||
49 | final IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, | ||
50 | final TupleMask groupMask, final TupleMask columnMask) { | ||
51 | super(reteContainer, operator, groupMask, columnMask); | ||
52 | this.memory = CollectionsFactory.createMap(); | ||
53 | // mailbox MUST be instantiated after the fields are all set | ||
54 | this.mailbox = instantiateMailbox(); | ||
55 | } | ||
56 | |||
57 | protected static class CumulativeAggregate<Accumulator, AggregateResult> { | ||
58 | // the accumulator storing the aggregands | ||
59 | protected Accumulator accumulator; | ||
60 | // the aggregate result at the timestamp where this cumulative aggregate is stored | ||
61 | protected AggregateResult result; | ||
62 | |||
63 | private CumulativeAggregate(final Accumulator accumulator, final AggregateResult result) { | ||
64 | this.accumulator = accumulator; | ||
65 | this.result = result; | ||
66 | } | ||
67 | |||
68 | } | ||
69 | |||
70 | public Collection<Tuple> getGroups() { | ||
71 | return this.memory.keySet(); | ||
72 | } | ||
73 | |||
74 | public AggregateResult getLastResult(final Tuple group) { | ||
75 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupMap = this.memory.get(group); | ||
76 | if (groupMap == null) { | ||
77 | return null; | ||
78 | } else { | ||
79 | return groupMap.lastEntry().getValue().result; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | public Timestamp getLastTimestamp(final Tuple group) { | ||
84 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> groupMap = this.memory.get(group); | ||
85 | if (groupMap == null) { | ||
86 | return null; | ||
87 | } else { | ||
88 | return groupMap.lastEntry().getKey(); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | protected Mailbox instantiateMailbox() { | ||
94 | return new TimelyMailbox(this, this.reteContainer); | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public void clear() { | ||
99 | this.mailbox.clear(); | ||
100 | this.memory.clear(); | ||
101 | this.children.clear(); | ||
102 | this.childMailboxes.clear(); | ||
103 | } | ||
104 | |||
105 | protected void propagateWithChecks(final Tuple group, final Timestamp timestamp, | ||
106 | final AggregateResult previousOldResult, final AggregateResult previousNewResult, | ||
107 | final AggregateResult currentOldResult, final AggregateResult currentNewResult) { | ||
108 | final boolean jumpDown = Objects.equals(previousNewResult, currentOldResult); | ||
109 | final boolean jumpUp = Objects.equals(previousOldResult, currentNewResult); | ||
110 | final boolean resultsDiffer = !Objects.equals(currentOldResult, currentNewResult); | ||
111 | |||
112 | // uniqueness enforcement is happening here | ||
113 | if ((resultsDiffer || jumpDown) && !Objects.equals(previousOldResult, currentOldResult)) { | ||
114 | propagate(Direction.DELETE, group, currentOldResult, timestamp); | ||
115 | } | ||
116 | if ((resultsDiffer || jumpUp) && !Objects.equals(previousNewResult, currentNewResult)) { | ||
117 | propagate(Direction.INSERT, group, currentNewResult, timestamp); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /** | ||
122 | * Returns the aggregation architecture-specific accumulator at the specified timestamp for the given group. | ||
123 | */ | ||
124 | protected abstract Accumulator getAccumulator(final Tuple group, final Timestamp timestamp); | ||
125 | |||
126 | protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) { | ||
127 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group); | ||
128 | if (entryMap == null) { | ||
129 | return null; | ||
130 | } else { | ||
131 | CumulativeAggregate<Accumulator, AggregateResult> entry = null; | ||
132 | if (lower) { | ||
133 | final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> lowerEntry = entryMap | ||
134 | .lowerEntry(timestamp); | ||
135 | if (lowerEntry != null) { | ||
136 | entry = lowerEntry.getValue(); | ||
137 | } | ||
138 | } else { | ||
139 | entry = entryMap.get(timestamp); | ||
140 | } | ||
141 | if (entry == null) { | ||
142 | return null; | ||
143 | } else { | ||
144 | return entry.result; | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | protected AggregateResult getResult(final Tuple group, final Timestamp timestamp, final boolean lower) { | ||
150 | final AggregateResult result = getResultRaw(group, timestamp, lower); | ||
151 | if (result == null) { | ||
152 | return NEUTRAL; | ||
153 | } else { | ||
154 | return result; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | protected AggregateResult getResult(final Tuple group, final Timestamp timestamp) { | ||
159 | return getResult(group, timestamp, false); | ||
160 | } | ||
161 | |||
162 | protected void storeIfNotNeutral(final Tuple group, final Accumulator accumulator, final AggregateResult value, | ||
163 | final Timestamp timestamp) { | ||
164 | TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group); | ||
165 | if (operator.isNeutral(accumulator)) { | ||
166 | if (entryMap != null) { | ||
167 | entryMap.remove(timestamp); | ||
168 | if (entryMap.isEmpty()) { | ||
169 | this.memory.remove(group); | ||
170 | } | ||
171 | } | ||
172 | } else { | ||
173 | if (entryMap == null) { | ||
174 | entryMap = CollectionsFactory.createTreeMap(); | ||
175 | this.memory.put(group, entryMap); | ||
176 | } | ||
177 | entryMap.put(timestamp, new CumulativeAggregate<>(accumulator, value)); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | @Override | ||
182 | public Tuple getAggregateTuple(final Tuple group) { | ||
183 | return tupleFromAggregateResult(group, getResult(group, Timestamp.ZERO)); | ||
184 | } | ||
185 | |||
186 | @Override | ||
187 | public AggregateResult getAggregateResult(final Tuple group) { | ||
188 | return getResult(group, Timestamp.ZERO); | ||
189 | } | ||
190 | |||
191 | @Override | ||
192 | public Map<AggregateResult, Timeline<Timestamp>> getAggregateResultTimeline(final Tuple group) { | ||
193 | final TreeMap<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entryMap = this.memory.get(group); | ||
194 | if (entryMap == null) { | ||
195 | return Collections.emptyMap(); | ||
196 | } else { | ||
197 | final Map<AggregateResult, Timeline<Timestamp>> result = CollectionsFactory.createMap(); | ||
198 | for (final Entry<Timestamp, CumulativeAggregate<Accumulator, AggregateResult>> entry : entryMap | ||
199 | .descendingMap().entrySet()) { | ||
200 | result.put(entry.getValue().result, Timelines.createFrom(entry.getKey())); | ||
201 | } | ||
202 | return result; | ||
203 | } | ||
204 | } | ||
205 | |||
206 | @Override | ||
207 | public Map<Tuple, Timeline<Timestamp>> getAggregateTupleTimeline(final Tuple group) { | ||
208 | final Map<AggregateResult, Timeline<Timestamp>> resultTimelines = getAggregateResultTimeline(group); | ||
209 | return new GroupedMap<AggregateResult, Timeline<Timestamp>>(group, resultTimelines, this.runtimeContext); | ||
210 | } | ||
211 | |||
212 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java new file mode 100644 index 00000000..7bbf74ea --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java | |||
@@ -0,0 +1,26 @@ | |||
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.rete.boundary; | ||
11 | |||
12 | /** | ||
13 | * For objects that connect a RETE implementation to the underlying model. | ||
14 | * | ||
15 | * @author Gabor Bergmann | ||
16 | * | ||
17 | */ | ||
18 | public interface Disconnectable { | ||
19 | |||
20 | /** | ||
21 | * Disconnects this rete engine component from the underlying model. Disconnecting enables the garbage collection | ||
22 | * mechanisms to dispose of the rete network. | ||
23 | */ | ||
24 | void disconnect(); | ||
25 | |||
26 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java new file mode 100644 index 00000000..51f89b52 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java | |||
@@ -0,0 +1,209 @@ | |||
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.rete.boundary; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; | ||
25 | import tools.refinery.viatra.runtime.rete.network.Network; | ||
26 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
27 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
28 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
29 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
31 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
32 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
33 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
34 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
35 | |||
36 | /** | ||
37 | * An input node representing an enumerable extensional input relation and receiving external updates. | ||
38 | * | ||
39 | * <p> | ||
40 | * Contains those tuples that are in the extensional relation identified by the input key, and also conform to the | ||
41 | * global seed (if any). | ||
42 | * | ||
43 | * @author Bergmann Gabor | ||
44 | * | ||
45 | */ | ||
46 | public class ExternalInputEnumeratorNode extends StandardNode | ||
47 | implements Disconnectable, Receiver, IQueryRuntimeContextListener { | ||
48 | |||
49 | private IQueryRuntimeContext context = null; | ||
50 | private IInputKey inputKey; | ||
51 | private Tuple globalSeed; | ||
52 | private InputConnector inputConnector; | ||
53 | private Network network; | ||
54 | private Address<? extends Receiver> myAddress; | ||
55 | private boolean parallelExecutionEnabled; | ||
56 | /** | ||
57 | * @since 1.6 | ||
58 | */ | ||
59 | protected final Mailbox mailbox; | ||
60 | private final IQueryBackendContext qBackendContext; | ||
61 | |||
62 | public ExternalInputEnumeratorNode(ReteContainer reteContainer) { | ||
63 | super(reteContainer); | ||
64 | myAddress = Address.of(this); | ||
65 | network = reteContainer.getNetwork(); | ||
66 | inputConnector = network.getInputConnector(); | ||
67 | qBackendContext = network.getEngine().getBackendContext(); | ||
68 | mailbox = instantiateMailbox(); | ||
69 | reteContainer.registerClearable(mailbox); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own | ||
74 | * mailbox implementation. | ||
75 | * | ||
76 | * @return the mailbox | ||
77 | * @since 2.0 | ||
78 | */ | ||
79 | protected Mailbox instantiateMailbox() { | ||
80 | if (this.reteContainer.isTimelyEvaluation()) { | ||
81 | return new TimelyMailbox(this, this.reteContainer); | ||
82 | } else { | ||
83 | return new BehaviorChangingMailbox(this, this.reteContainer); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public Mailbox getMailbox() { | ||
89 | return this.mailbox; | ||
90 | } | ||
91 | |||
92 | public void connectThroughContext(ReteEngine engine, IInputKey inputKey, Tuple globalSeed) { | ||
93 | this.inputKey = inputKey; | ||
94 | this.globalSeed = globalSeed; | ||
95 | setTag(inputKey); | ||
96 | |||
97 | final IQueryRuntimeContext context = engine.getRuntimeContext(); | ||
98 | if (!context.getMetaContext().isEnumerable(inputKey)) | ||
99 | throw new IllegalArgumentException(this.getClass().getSimpleName() | ||
100 | + " only applicable for enumerable input keys; received instead " + inputKey); | ||
101 | |||
102 | this.context = context; | ||
103 | this.parallelExecutionEnabled = engine.isParallelExecutionEnabled(); | ||
104 | |||
105 | engine.addDisconnectable(this); | ||
106 | context.addUpdateListener(inputKey, globalSeed, this); | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public void disconnect() { | ||
111 | if (context != null) { // if connected | ||
112 | context.removeUpdateListener(inputKey, globalSeed, this); | ||
113 | context = null; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * @since 2.2 | ||
119 | */ | ||
120 | protected Iterable<Tuple> getTuplesInternal() { | ||
121 | Iterable<Tuple> tuples = null; | ||
122 | |||
123 | if (context != null) { // if connected | ||
124 | if (globalSeed == null) { | ||
125 | tuples = context.enumerateTuples(inputKey, TupleMask.empty(inputKey.getArity()), | ||
126 | Tuples.staticArityFlatTupleOf()); | ||
127 | } else { | ||
128 | final TupleMask mask = TupleMask.fromNonNullIndices(globalSeed); | ||
129 | tuples = context.enumerateTuples(inputKey, mask, mask.transform(globalSeed)); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | return tuples; | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
138 | final Iterable<Tuple> tuples = getTuplesInternal(); | ||
139 | if (tuples != null) { | ||
140 | for (final Tuple tuple : tuples) { | ||
141 | collector.add(tuple); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | @Override | ||
147 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
148 | final Iterable<Tuple> tuples = getTuplesInternal(); | ||
149 | if (tuples != null) { | ||
150 | for (final Tuple tuple : tuples) { | ||
151 | collector.put(tuple, Timestamp.INSERT_AT_ZERO_TIMELINE); | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | |||
156 | /* Update from runtime context */ | ||
157 | @Override | ||
158 | public void update(IInputKey key, Tuple update, boolean isInsertion) { | ||
159 | if (parallelExecutionEnabled) { | ||
160 | // send back to myself as an official external update, and then propagate it transparently | ||
161 | network.sendExternalUpdate(myAddress, direction(isInsertion), update); | ||
162 | } else { | ||
163 | if (qBackendContext.areUpdatesDelayed()) { | ||
164 | // post the update into the mailbox of the node | ||
165 | mailbox.postMessage(direction(isInsertion), update, Timestamp.ZERO); | ||
166 | } else { | ||
167 | // just propagate the input | ||
168 | update(direction(isInsertion), update, Timestamp.ZERO); | ||
169 | } | ||
170 | // if the the update method is called from within a delayed execution, | ||
171 | // the following invocation will be a no-op | ||
172 | network.waitForReteTermination(); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | private static Direction direction(boolean isInsertion) { | ||
177 | return isInsertion ? Direction.INSERT : Direction.DELETE; | ||
178 | } | ||
179 | |||
180 | /* Self-addressed from network */ | ||
181 | @Override | ||
182 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
183 | propagateUpdate(direction, updateElement, timestamp); | ||
184 | } | ||
185 | |||
186 | @Override | ||
187 | public void appendParent(Supplier supplier) { | ||
188 | throw new UnsupportedOperationException("Input nodes can't have parents"); | ||
189 | } | ||
190 | |||
191 | @Override | ||
192 | public void removeParent(Supplier supplier) { | ||
193 | throw new UnsupportedOperationException("Input nodes can't have parents"); | ||
194 | } | ||
195 | |||
196 | @Override | ||
197 | public Collection<Supplier> getParents() { | ||
198 | return Collections.emptySet(); | ||
199 | } | ||
200 | |||
201 | public IInputKey getInputKey() { | ||
202 | return inputKey; | ||
203 | } | ||
204 | |||
205 | public Tuple getGlobalSeed() { | ||
206 | return globalSeed; | ||
207 | } | ||
208 | |||
209 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java new file mode 100644 index 00000000..57e06911 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java | |||
@@ -0,0 +1,68 @@ | |||
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.rete.boundary; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
15 | import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; | ||
16 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
17 | import tools.refinery.viatra.runtime.rete.single.FilterNode; | ||
18 | |||
19 | /** | ||
20 | * A filter node representing a (stateless, typically non-enumerable) extensional input relation. | ||
21 | * | ||
22 | * <p> Contains those tuples of its parents, that (when transformed by a mask, if given) are present in the extensional relation identified by the input key. | ||
23 | * | ||
24 | * @author Bergmann Gabor | ||
25 | * | ||
26 | */ | ||
27 | public class ExternalInputStatelessFilterNode extends FilterNode implements Disconnectable { | ||
28 | |||
29 | IQueryRuntimeContext context = null; | ||
30 | IInputKey inputKey; | ||
31 | private InputConnector inputConnector; | ||
32 | private TupleMask mask; | ||
33 | |||
34 | public ExternalInputStatelessFilterNode(ReteContainer reteContainer, TupleMask mask) { | ||
35 | super(reteContainer); | ||
36 | this.mask = mask; | ||
37 | this.inputConnector = reteContainer.getNetwork().getInputConnector(); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public boolean check(Tuple ps) { | ||
42 | if (mask != null) | ||
43 | ps = mask.transform(ps); | ||
44 | return context.containsTuple(inputKey, ps); | ||
45 | } | ||
46 | |||
47 | |||
48 | public void connectThroughContext(ReteEngine engine, IInputKey inputKey) { | ||
49 | this.inputKey = inputKey; | ||
50 | setTag(inputKey); | ||
51 | |||
52 | final IQueryRuntimeContext context = engine.getRuntimeContext(); | ||
53 | if (!context.getMetaContext().isStateless(inputKey)) | ||
54 | throw new IllegalArgumentException( | ||
55 | this.getClass().getSimpleName() + | ||
56 | " only applicable for stateless input keys; received instead " + | ||
57 | inputKey); | ||
58 | |||
59 | this.context = context; | ||
60 | |||
61 | engine.addDisconnectable(this); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public void disconnect() { | ||
66 | this.context = null; | ||
67 | } | ||
68 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java new file mode 100644 index 00000000..c044850f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java | |||
@@ -0,0 +1,208 @@ | |||
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.rete.boundary; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | import java.util.stream.Stream; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.rete.network.Network; | ||
21 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
22 | import tools.refinery.viatra.runtime.rete.recipes.InputFilterRecipe; | ||
23 | import tools.refinery.viatra.runtime.rete.recipes.InputRecipe; | ||
24 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
25 | |||
26 | /** | ||
27 | * A class responsible for connecting input nodes to the runtime context. | ||
28 | * | ||
29 | * @author Bergmann Gabor | ||
30 | * | ||
31 | */ | ||
32 | public final class InputConnector { | ||
33 | Network network; | ||
34 | |||
35 | private Map<IInputKey, Map<Tuple, Address<ExternalInputEnumeratorNode>>> externalInputRoots = CollectionsFactory.createMap(); | ||
36 | |||
37 | // /* | ||
38 | // * arity:1 used as simple entity constraints label is the object representing the type null label means all entities | ||
39 | // * regardless of type (global supertype), if allowed | ||
40 | // */ | ||
41 | // protected Map<Object, Address<? extends Tunnel>> unaryRoots = CollectionsFactory.getMap(); | ||
42 | // /* | ||
43 | // * arity:3 (rel, from, to) used as VPM relation constraints null label means all relations regardless of type | ||
44 | // * (global supertype) | ||
45 | // */ | ||
46 | // protected Map<Object, Address<? extends Tunnel>> ternaryEdgeRoots = CollectionsFactory.getMap(); | ||
47 | // /* | ||
48 | // * arity:2 (from, to) not used over VPM; can be used as EMF references for instance label is the object representing | ||
49 | // * the type null label means all entities regardless of type if allowed (global supertype), if allowed | ||
50 | // */ | ||
51 | // protected Map<Object, Address<? extends Tunnel>> binaryEdgeRoots = CollectionsFactory.getMap(); | ||
52 | // | ||
53 | // protected Address<? extends Tunnel> containmentRoot = null; | ||
54 | // protected Address<? extends Supplier> containmentTransitiveRoot = null; | ||
55 | // protected Address<? extends Tunnel> instantiationRoot = null; | ||
56 | // protected Address<? extends Supplier> instantiationTransitiveRoot = null; | ||
57 | // protected Address<? extends Tunnel> generalizationRoot = null; | ||
58 | // protected Address<? extends Supplier> generalizationTransitiveRoot = null; | ||
59 | |||
60 | |||
61 | public InputConnector(Network network) { | ||
62 | super(); | ||
63 | this.network = network; | ||
64 | } | ||
65 | |||
66 | |||
67 | public Network getNetwork() { | ||
68 | return network; | ||
69 | } | ||
70 | |||
71 | |||
72 | /** | ||
73 | * Connects a given input filter node to the external input source. | ||
74 | */ | ||
75 | public void connectInputFilter(InputFilterRecipe recipe, Node freshNode) { | ||
76 | final ExternalInputStatelessFilterNode inputNode = (ExternalInputStatelessFilterNode)freshNode; | ||
77 | |||
78 | IInputKey inputKey = (IInputKey) recipe.getInputKey(); | ||
79 | inputNode.connectThroughContext(network.getEngine(), inputKey); | ||
80 | } | ||
81 | |||
82 | |||
83 | /** | ||
84 | * Connects a given input enumerator node to the external input source. | ||
85 | */ | ||
86 | public void connectInput(InputRecipe recipe, Node freshNode) { | ||
87 | final ExternalInputEnumeratorNode inputNode = (ExternalInputEnumeratorNode)freshNode; | ||
88 | |||
89 | IInputKey inputKey = (IInputKey) recipe.getInputKey(); | ||
90 | Tuple seed = nopSeed(inputKey); // no preseeding as of now | ||
91 | final Address<ExternalInputEnumeratorNode> freshAddress = Address.of(inputNode); | ||
92 | externalInputRoots.computeIfAbsent(inputKey, k -> CollectionsFactory.createMap()).put(seed, freshAddress); | ||
93 | inputNode.connectThroughContext(network.getEngine(), inputKey, seed); | ||
94 | |||
95 | // final Address<Tunnel> freshAddress = Address.of((Tunnel)freshNode); | ||
96 | // if (recipe instanceof TypeInputRecipe) { | ||
97 | // final Object typeKey = ((TypeInputRecipe) recipe).getTypeKey(); | ||
98 | // | ||
99 | // if (recipe instanceof UnaryInputRecipe) { | ||
100 | // unaryRoots.put(typeKey, freshAddress); | ||
101 | // new EntityFeeder(freshAddress, this, typeKey).feed(); | ||
102 | //// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { | ||
103 | //// Collection<? extends Object> subTypes = context.enumerateDirectUnarySubtypes(typeObject); | ||
104 | //// | ||
105 | //// for (Object subType : subTypes) { | ||
106 | //// Address<? extends Tunnel> subRoot = accessUnaryRoot(subType); | ||
107 | //// network.connectRemoteNodes(subRoot, tn, true); | ||
108 | //// } | ||
109 | //// } | ||
110 | // } else if (recipe instanceof BinaryInputRecipe) { | ||
111 | // binaryEdgeRoots.put(typeKey, freshAddress); | ||
112 | // externalInputRoots.put(rowKey, columnKey, freshAddress); | ||
113 | // new ReferenceFeeder(freshAddress, this, typeKey).feed(); | ||
114 | // // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { | ||
115 | // // Collection<? extends Object> subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject); | ||
116 | // // | ||
117 | // // for (Object subType : subTypes) { | ||
118 | // // Address<? extends Tunnel> subRoot = accessTernaryEdgeRoot(subType); | ||
119 | // // network.connectRemoteNodes(subRoot, tn, true); | ||
120 | // // } | ||
121 | // // } | ||
122 | // } | ||
123 | // | ||
124 | // | ||
125 | // } | ||
126 | |||
127 | } | ||
128 | |||
129 | // /** | ||
130 | // * fetches the entity Root node under specified label; returns null if it doesn't exist yet | ||
131 | // */ | ||
132 | // public Address<? extends Tunnel> getUnaryRoot(Object label) { | ||
133 | // return unaryRoots.get(label); | ||
134 | // } | ||
135 | // | ||
136 | // public Collection<Address<? extends Tunnel>> getAllUnaryRoots() { | ||
137 | // return unaryRoots.values(); | ||
138 | // } | ||
139 | // | ||
140 | // /** | ||
141 | // * fetches the relation Root node under specified label; returns null if it doesn't exist yet | ||
142 | // */ | ||
143 | // public Address<? extends Tunnel> getTernaryEdgeRoot(Object label) { | ||
144 | // return ternaryEdgeRoots.get(label); | ||
145 | // } | ||
146 | // | ||
147 | // public Collection<Address<? extends Tunnel>> getAllTernaryEdgeRoots() { | ||
148 | // return ternaryEdgeRoots.values(); | ||
149 | // } | ||
150 | // | ||
151 | // /** | ||
152 | // * fetches the reference Root node under specified label; returns null if it doesn't exist yet | ||
153 | // */ | ||
154 | // public Address<? extends Tunnel> getBinaryEdgeRoot(Object label) { | ||
155 | // return binaryEdgeRoots.get(label); | ||
156 | // } | ||
157 | // | ||
158 | // public Collection<Address<? extends Tunnel>> getAllBinaryEdgeRoots() { | ||
159 | // return binaryEdgeRoots.values(); | ||
160 | // } | ||
161 | // | ||
162 | // | ||
163 | // public Address<? extends Tunnel> getContainmentRoot() { | ||
164 | // return containmentRoot; | ||
165 | // } | ||
166 | // | ||
167 | // | ||
168 | // public Address<? extends Supplier> getContainmentTransitiveRoot() { | ||
169 | // return containmentTransitiveRoot; | ||
170 | // } | ||
171 | // | ||
172 | // | ||
173 | // public Address<? extends Tunnel> getInstantiationRoot() { | ||
174 | // return instantiationRoot; | ||
175 | // } | ||
176 | // | ||
177 | // | ||
178 | // public Address<? extends Supplier> getInstantiationTransitiveRoot() { | ||
179 | // return instantiationTransitiveRoot; | ||
180 | // } | ||
181 | // | ||
182 | // | ||
183 | // public Address<? extends Tunnel> getGeneralizationRoot() { | ||
184 | // return generalizationRoot; | ||
185 | // } | ||
186 | |||
187 | |||
188 | public Stream<Address<ExternalInputEnumeratorNode>> getAllExternalInputNodes() { | ||
189 | return externalInputRoots.values().stream().flatMap(map -> map.values().stream()); | ||
190 | } | ||
191 | public Collection<Address<ExternalInputEnumeratorNode>> getAllExternalInputNodesForKey(IInputKey inputKey) { | ||
192 | return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).values(); | ||
193 | } | ||
194 | public Address<ExternalInputEnumeratorNode> getExternalInputNodeForKeyUnseeded(IInputKey inputKey) { | ||
195 | return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(nopSeed(inputKey)); | ||
196 | } | ||
197 | public Address<ExternalInputEnumeratorNode> getExternalInputNode(IInputKey inputKey, Tuple seed) { | ||
198 | if (seed == null) seed = nopSeed(inputKey); | ||
199 | return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(seed); | ||
200 | } | ||
201 | |||
202 | |||
203 | Tuple nopSeed(IInputKey inputKey) { | ||
204 | return Tuples.flatTupleOf(new Object[inputKey.getArity()]); | ||
205 | } | ||
206 | |||
207 | |||
208 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java new file mode 100644 index 00000000..fe9c795e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java | |||
@@ -0,0 +1,551 @@ | |||
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.rete.boundary; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
17 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
22 | import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; | ||
23 | import tools.refinery.viatra.runtime.rete.network.Network; | ||
24 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
25 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
26 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
27 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
28 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
29 | import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; | ||
30 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
31 | |||
32 | /** | ||
33 | * Responsible for the storage, maintenance and communication of the nodes of the network that are accessible form the | ||
34 | * outside for various reasons. | ||
35 | * | ||
36 | * @author Gabor Bergmann | ||
37 | * | ||
38 | * <p> TODO: should eventually be merged into {@link InputConnector} and deleted | ||
39 | * | ||
40 | */ | ||
41 | public class ReteBoundary /*implements IPatternMatcherRuntimeContextListener*/ { | ||
42 | |||
43 | protected ReteEngine engine; | ||
44 | protected Network network; | ||
45 | protected ReteContainer headContainer; | ||
46 | |||
47 | public ReteContainer getHeadContainer() { | ||
48 | return headContainer; | ||
49 | } | ||
50 | |||
51 | protected final InputConnector inputConnector; | ||
52 | |||
53 | |||
54 | protected Map<SubPlan, Address<? extends Supplier>> subplanToAddressMapping; | ||
55 | |||
56 | |||
57 | /** | ||
58 | * SubPlans of parent nodes that have the key node as their child. For RETE --> SubPlan traceability, mainly at production | ||
59 | * nodes. | ||
60 | */ | ||
61 | protected Map<Address<? extends Receiver>, Set<SubPlan>> parentPlansOfReceiver; | ||
62 | |||
63 | /** | ||
64 | * Prerequisite: engine has its network and framework fields initialized | ||
65 | */ | ||
66 | public ReteBoundary(ReteEngine engine) { | ||
67 | super(); | ||
68 | this.engine = engine; | ||
69 | this.network = engine.getReteNet(); | ||
70 | this.headContainer = network.getHeadContainer(); | ||
71 | inputConnector = network.getInputConnector(); | ||
72 | |||
73 | this.parentPlansOfReceiver = CollectionsFactory.createMap(); | ||
74 | |||
75 | // productionsScoped = new HashMap<GTPattern, Map<Map<Integer,Scope>,Address<? extends Production>>>(); | ||
76 | subplanToAddressMapping = CollectionsFactory.createMap(); | ||
77 | |||
78 | } | ||
79 | |||
80 | public Collection<? extends RecipeTraceInfo> getAllProductionNodes() { | ||
81 | return engine.getCompiler().getCachedCompiledQueries().values(); | ||
82 | } | ||
83 | |||
84 | // /** | ||
85 | // * accesses the entity Root node under specified label; creates the node if it doesn't exist yet | ||
86 | // */ | ||
87 | // public Address<? extends Tunnel> accessUnaryRoot(Object typeObject) { | ||
88 | // Address<? extends Tunnel> tn; | ||
89 | // tn = unaryRoots.get(typeObject); | ||
90 | // if (tn == null) { | ||
91 | // tn = headContainer.getProvisioner().newUniquenessEnforcerNode(1, typeObject); | ||
92 | // unaryRoots.put(typeObject, tn); | ||
93 | // | ||
94 | // new EntityFeeder(tn, context, network, this, typeObject).feed(); | ||
95 | // | ||
96 | // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { | ||
97 | // Collection<? extends Object> subTypes = context.enumerateDirectUnarySubtypes(typeObject); | ||
98 | // | ||
99 | // for (Object subType : subTypes) { | ||
100 | // Address<? extends Tunnel> subRoot = accessUnaryRoot(subType); | ||
101 | // network.connectRemoteNodes(subRoot, tn, true); | ||
102 | // } | ||
103 | // } | ||
104 | // | ||
105 | // } | ||
106 | // return tn; | ||
107 | // } | ||
108 | // | ||
109 | // /** | ||
110 | // * accesses the relation Root node under specified label; creates the node if it doesn't exist yet | ||
111 | // */ | ||
112 | // public Address<? extends Tunnel> accessTernaryEdgeRoot(Object typeObject) { | ||
113 | // Address<? extends Tunnel> tn; | ||
114 | // tn = ternaryEdgeRoots.get(typeObject); | ||
115 | // if (tn == null) { | ||
116 | // tn = headContainer.getProvisioner().newUniquenessEnforcerNode(3, typeObject); | ||
117 | // ternaryEdgeRoots.put(typeObject, tn); | ||
118 | // | ||
119 | // new RelationFeeder(tn, context, network, this, typeObject).feed(); | ||
120 | // | ||
121 | // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { | ||
122 | // Collection<? extends Object> subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject); | ||
123 | // | ||
124 | // for (Object subType : subTypes) { | ||
125 | // Address<? extends Tunnel> subRoot = accessTernaryEdgeRoot(subType); | ||
126 | // network.connectRemoteNodes(subRoot, tn, true); | ||
127 | // } | ||
128 | // } | ||
129 | // } | ||
130 | // return tn; | ||
131 | // } | ||
132 | // | ||
133 | // /** | ||
134 | // * accesses the reference Root node under specified label; creates the node if it doesn't exist yet | ||
135 | // */ | ||
136 | // public Address<? extends Tunnel> accessBinaryEdgeRoot(Object typeObject) { | ||
137 | // Address<? extends Tunnel> tn; | ||
138 | // tn = binaryEdgeRoots.get(typeObject); | ||
139 | // if (tn == null) { | ||
140 | // tn = headContainer.getProvisioner().newUniquenessEnforcerNode(2, typeObject); | ||
141 | // binaryEdgeRoots.put(typeObject, tn); | ||
142 | // | ||
143 | // new ReferenceFeeder(tn, context, network, this, typeObject).feed(); | ||
144 | // | ||
145 | // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { | ||
146 | // Collection<? extends Object> subTypes = context.enumerateDirectBinaryEdgeSubtypes(typeObject); | ||
147 | // | ||
148 | // for (Object subType : subTypes) { | ||
149 | // Address<? extends Tunnel> subRoot = accessBinaryEdgeRoot(subType); | ||
150 | // network.connectRemoteNodes(subRoot, tn, true); | ||
151 | // } | ||
152 | // } | ||
153 | // } | ||
154 | // return tn; | ||
155 | // } | ||
156 | // | ||
157 | // /** | ||
158 | // * accesses the special direct containment relation Root node; creates the node if it doesn't exist yet | ||
159 | // */ | ||
160 | // public Address<? extends Tunnel> accessContainmentRoot() { | ||
161 | // if (containmentRoot == null) { | ||
162 | // // containment: relation quasi-type | ||
163 | // containmentRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$containment"); | ||
164 | // | ||
165 | // new ContainmentFeeder(containmentRoot, context, network, this).feed(); | ||
166 | // } | ||
167 | // return containmentRoot; | ||
168 | // } | ||
169 | // | ||
170 | // /** | ||
171 | // * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet | ||
172 | // */ | ||
173 | // public Address<? extends Supplier> accessContainmentTransitiveRoot() { | ||
174 | // if (containmentTransitiveRoot == null) { | ||
175 | // // transitive containment: derived | ||
176 | // Address<? extends Tunnel> containmentTransitiveRoot = headContainer.getProvisioner().newUniquenessEnforcerNode( | ||
177 | // 2, "$containmentTransitive"); | ||
178 | // network.connectRemoteNodes(accessContainmentRoot(), containmentTransitiveRoot, true); | ||
179 | // | ||
180 | // final int[] actLI = { 1 }; | ||
181 | // final int arcLIw = 2; | ||
182 | // final int[] actRI = { 0 }; | ||
183 | // final int arcRIw = 2; | ||
184 | // Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer( | ||
185 | // accessContainmentRoot(), new TupleMask(actLI, arcLIw)); | ||
186 | // Address<? extends IterableIndexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer( | ||
187 | // containmentTransitiveRoot, new TupleMask(actRI, arcRIw)); | ||
188 | // | ||
189 | // final int[] actRIcomp = { 1 }; | ||
190 | // final int arcRIwcomp = 2; | ||
191 | // TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp); | ||
192 | // | ||
193 | // Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot, | ||
194 | // complementerMask); | ||
195 | // | ||
196 | // final int[] mask = { 0, 2 }; | ||
197 | // final int maskw = 3; | ||
198 | // Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw)); | ||
199 | // network.connectRemoteNodes(tr, containmentTransitiveRoot, true); | ||
200 | // | ||
201 | // this.containmentTransitiveRoot = containmentTransitiveRoot; // cast | ||
202 | // // back | ||
203 | // // to | ||
204 | // // Supplier | ||
205 | // } | ||
206 | // return containmentTransitiveRoot; | ||
207 | // } | ||
208 | // | ||
209 | // /** | ||
210 | // * accesses the special instantiation relation Root node; creates the node if it doesn't exist yet | ||
211 | // */ | ||
212 | // public Address<? extends Tunnel> accessInstantiationRoot() { | ||
213 | // if (instantiationRoot == null) { | ||
214 | // // instantiation: relation quasi-type | ||
215 | // instantiationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$instantiation"); | ||
216 | // | ||
217 | // new InstantiationFeeder(instantiationRoot, context, network, this).feed(); | ||
218 | // } | ||
219 | // return instantiationRoot; | ||
220 | // } | ||
221 | // | ||
222 | // /** | ||
223 | // * accesses the special transitive instantiation relation Root node; creates the node if it doesn't exist yet | ||
224 | // * InstantiationTransitive = Instantiation o (Generalization)^* | ||
225 | // */ | ||
226 | // public Address<? extends Supplier> accessInstantiationTransitiveRoot() { | ||
227 | // if (instantiationTransitiveRoot == null) { | ||
228 | // // transitive instantiation: derived | ||
229 | // Address<? extends Tunnel> instantiationTransitiveRoot = headContainer.getProvisioner() | ||
230 | // .newUniquenessEnforcerNode(2, "$instantiationTransitive"); | ||
231 | // network.connectRemoteNodes(accessInstantiationRoot(), instantiationTransitiveRoot, true); | ||
232 | // | ||
233 | // final int[] actLI = { 1 }; | ||
234 | // final int arcLIw = 2; | ||
235 | // final int[] actRI = { 0 }; | ||
236 | // final int arcRIw = 2; | ||
237 | // Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer( | ||
238 | // accessGeneralizationRoot(), new TupleMask(actLI, arcLIw)); | ||
239 | // Address<? extends Indexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer( | ||
240 | // instantiationTransitiveRoot, new TupleMask(actRI, arcRIw)); | ||
241 | // | ||
242 | // final int[] actRIcomp = { 1 }; | ||
243 | // final int arcRIwcomp = 2; | ||
244 | // TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp); | ||
245 | // | ||
246 | // Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot, | ||
247 | // complementerMask); | ||
248 | // | ||
249 | // final int[] mask = { 0, 2 }; | ||
250 | // final int maskw = 3; | ||
251 | // Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT, | ||
252 | // new TupleMask(mask, maskw)); | ||
253 | // network.connectRemoteNodes(tr, instantiationTransitiveRoot, true); | ||
254 | // | ||
255 | // this.instantiationTransitiveRoot = instantiationTransitiveRoot; // cast | ||
256 | // // back | ||
257 | // // to | ||
258 | // // Supplier | ||
259 | // } | ||
260 | // return instantiationTransitiveRoot; | ||
261 | // } | ||
262 | // | ||
263 | // /** | ||
264 | // * accesses the special generalization relation Root node; creates the node if it doesn't exist yet | ||
265 | // */ | ||
266 | // public Address<? extends Tunnel> accessGeneralizationRoot() { | ||
267 | // if (generalizationRoot == null) { | ||
268 | // // generalization: relation quasi-type | ||
269 | // generalizationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$generalization"); | ||
270 | // | ||
271 | // new GeneralizationFeeder(generalizationRoot, context, network, this).feed(); | ||
272 | // } | ||
273 | // return generalizationRoot; | ||
274 | // } | ||
275 | // | ||
276 | // /** | ||
277 | // * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet | ||
278 | // */ | ||
279 | // public Address<? extends Supplier> accessGeneralizationTransitiveRoot() { | ||
280 | // if (generalizationTransitiveRoot == null) { | ||
281 | // // transitive generalization: derived | ||
282 | // Address<? extends Tunnel> generalizationTransitiveRoot = headContainer.getProvisioner() | ||
283 | // .newUniquenessEnforcerNode(2, "$generalizationTransitive"); | ||
284 | // network.connectRemoteNodes(accessGeneralizationRoot(), generalizationTransitiveRoot, true); | ||
285 | // | ||
286 | // final int[] actLI = { 1 }; | ||
287 | // final int arcLIw = 2; | ||
288 | // final int[] actRI = { 0 }; | ||
289 | // final int arcRIw = 2; | ||
290 | // Address<? extends IterableIndexer> jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer( | ||
291 | // accessGeneralizationRoot(), new TupleMask(actLI, arcLIw)); | ||
292 | // Address<? extends Indexer> jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer( | ||
293 | // generalizationTransitiveRoot, new TupleMask(actRI, arcRIw)); | ||
294 | // | ||
295 | // final int[] actRIcomp = { 1 }; | ||
296 | // final int arcRIwcomp = 2; | ||
297 | // TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp); | ||
298 | // | ||
299 | // Address<? extends Supplier> andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot, | ||
300 | // complementerMask); | ||
301 | // | ||
302 | // final int[] mask = { 0, 2 }; | ||
303 | // final int maskw = 3; | ||
304 | // Address<? extends Supplier> tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw)); | ||
305 | // network.connectRemoteNodes(tr, generalizationTransitiveRoot, true); | ||
306 | // | ||
307 | // this.generalizationTransitiveRoot = generalizationTransitiveRoot; // cast | ||
308 | // // back | ||
309 | // // to | ||
310 | // // Supplier | ||
311 | // } | ||
312 | // return generalizationTransitiveRoot; | ||
313 | // } | ||
314 | |||
315 | // /** | ||
316 | // * Registers and publishes a supplier under specified label. | ||
317 | // */ | ||
318 | // public void publishSupplier(Supplier s, Object label) | ||
319 | // { | ||
320 | // publishedSuppliers.put(label, s); | ||
321 | // } | ||
322 | // | ||
323 | // /** | ||
324 | // * fetches the production node under specified label; | ||
325 | // * returns null if it doesn't exist yet | ||
326 | // */ | ||
327 | // public Production getProductionNode(Object label) | ||
328 | // { | ||
329 | // return productions.get(label); | ||
330 | // } | ||
331 | // | ||
332 | // /** | ||
333 | // * fetches the published supplier under specified label; | ||
334 | // * returns null if it doesn't exist yet | ||
335 | // */ | ||
336 | // public Supplier getPublishedSupplier(Object label) | ||
337 | // { | ||
338 | // return publishedSuppliers.get(label); | ||
339 | // } | ||
340 | |||
341 | /** | ||
342 | * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet | ||
343 | * @throws ViatraQueryRuntimeException | ||
344 | */ | ||
345 | public synchronized RecipeTraceInfo accessProductionTrace(PQuery query) | ||
346 | { | ||
347 | final CompiledQuery compiled = engine.getCompiler().getCompiledForm(query); | ||
348 | return compiled; | ||
349 | // RecipeTraceInfo pn; | ||
350 | // pn = queryPlans.get(query); | ||
351 | // if (pn == null) { | ||
352 | // pn = construct(query); | ||
353 | // TODO handle recursion by reinterpret-RecipeTrace | ||
354 | // queryPlans.put(query, pn); | ||
355 | // if (pn == null) { | ||
356 | // String[] args = { query.toString() }; | ||
357 | // throw new RetePatternBuildException("Unsuccessful planning of RETE construction recipe for query {1}", | ||
358 | // args, "Could not create RETE recipe plan.", query); | ||
359 | // } | ||
360 | // } | ||
361 | // return pn; | ||
362 | } | ||
363 | /** | ||
364 | * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet | ||
365 | * @throws ViatraQueryRuntimeException | ||
366 | */ | ||
367 | public synchronized Address<? extends ProductionNode> accessProductionNode(PQuery query) { | ||
368 | final RecipeTraceInfo productionTrace = accessProductionTrace(query); | ||
369 | return (Address<? extends ProductionNode>) headContainer.getProvisioner().getOrCreateNodeByRecipe(productionTrace); | ||
370 | } | ||
371 | |||
372 | // /** | ||
373 | // * creates the production node for the specified pattern Contract: only call from the builder (through Buildable) | ||
374 | // * responsible for building this pattern | ||
375 | // * | ||
376 | // * @throws PatternMatcherCompileTimeException | ||
377 | // * if production node is already created | ||
378 | // */ | ||
379 | // public synchronized Address<? extends Production> createProductionInternal(PQuery gtPattern) | ||
380 | // throws QueryPlannerException { | ||
381 | // if (queryPlans.containsKey(gtPattern)) { | ||
382 | // String[] args = { gtPattern.toString() }; | ||
383 | // throw new RetePatternBuildException("Multiple creation attempts of production node for {1}", args, | ||
384 | // "Duplicate RETE production node.", gtPattern); | ||
385 | // } | ||
386 | // | ||
387 | // Map<String, Integer> posMapping = engine.getBuilder().getPosMapping(gtPattern); | ||
388 | // Address<? extends Production> pn = headContainer.getProvisioner().newProductionNode(posMapping, gtPattern); | ||
389 | // queryPlans.put(gtPattern, pn); | ||
390 | // context.reportPatternDependency(gtPattern); | ||
391 | // | ||
392 | // return pn; | ||
393 | // } | ||
394 | |||
395 | // /** | ||
396 | // * accesses the production node for specified pattern and scope map; creates the node if | ||
397 | // * it doesn't exist yet | ||
398 | // */ | ||
399 | // public synchronized Address<? extends Production> accessProductionScoped( | ||
400 | // GTPattern gtPattern, Map<Integer, Scope> additionalScopeMap) throws PatternMatcherCompileTimeException { | ||
401 | // if (additionalScopeMap.isEmpty()) return accessProduction(gtPattern); | ||
402 | // | ||
403 | // Address<? extends Production> pn; | ||
404 | // | ||
405 | // Map<Map<Integer, Scope>, Address<? extends Production>> scopes = productionsScoped.get(gtPattern); | ||
406 | // if (scopes == null) { | ||
407 | // scopes = new HashMap<Map<Integer, Scope>, Address<? extends Production>>(); | ||
408 | // productionsScoped.put(gtPattern, scopes); | ||
409 | // } | ||
410 | // | ||
411 | // pn = scopes.get(additionalScopeMap); | ||
412 | // if (pn == null) { | ||
413 | // Address<? extends Production> unscopedProduction = accessProduction(gtPattern); | ||
414 | // | ||
415 | // HashMap<Object, Integer> posMapping = headContainer.resolveLocal(unscopedProduction).getPosMapping(); | ||
416 | // pn = headContainer.getLibrary().newProductionNode(posMapping); | ||
417 | // scopes.put(additionalScopeMap, pn); | ||
418 | // | ||
419 | // constructScoper(unscopedProduction, additionalScopeMap, pn); | ||
420 | // } | ||
421 | // return pn; | ||
422 | // } | ||
423 | |||
424 | // protected void constructScoper( | ||
425 | // Address<? extends Production> unscopedProduction, | ||
426 | // Map<Integer, Scope> additionalScopeMap, | ||
427 | // Address<? extends Production> production) | ||
428 | // throws PatternMatcherCompileTimeException { | ||
429 | // engine.reteNet.waitForReteTermination(); | ||
430 | // engine.builder.constructScoper(unscopedProduction, additionalScopeMap, production); | ||
431 | // } | ||
432 | |||
433 | // /** | ||
434 | // * Invalidates the subnet constructed for the recognition of a given | ||
435 | // pattern. | ||
436 | // * The pattern matcher will have to be rebuilt. | ||
437 | // * @param gtPattern the pattern whose matcher subnet should be invalidated | ||
438 | // */ | ||
439 | // public void invalidatePattern(GTPattern gtPattern) { | ||
440 | // Production production = null; | ||
441 | // try { | ||
442 | // production = accessProduction(gtPattern); | ||
443 | // } catch (PatternMatcherCompileTimeException e) { | ||
444 | // // this should not occur here, since we already have a production node | ||
445 | // e.printStackTrace(); | ||
446 | // } | ||
447 | // | ||
448 | // production.tearOff(); | ||
449 | // //production.setDirty(true); | ||
450 | // } | ||
451 | |||
452 | // updaters for change notification | ||
453 | // if the corresponding rete input isn't created yet, call is ignored | ||
454 | |||
455 | private static Direction direction(boolean isInsertion) { | ||
456 | return isInsertion ? Direction.INSERT : Direction.DELETE; | ||
457 | } | ||
458 | |||
459 | // @Override | ||
460 | // public void updateUnary(boolean isInsertion, Object entity, Object typeObject) { | ||
461 | // Address<? extends Tunnel> root = inputConnector.getUnaryRoot(typeObject); | ||
462 | // if (root != null) { | ||
463 | // network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(entity))); | ||
464 | // if (!engine.isParallelExecutionEnabled()) | ||
465 | // network.waitForReteTermination(); | ||
466 | // } | ||
467 | // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) { | ||
468 | // for (Object superType : context.enumerateDirectUnarySupertypes(typeObject)) { | ||
469 | // updateUnary(isInsertion, entity, superType); | ||
470 | // } | ||
471 | // } | ||
472 | // } | ||
473 | // | ||
474 | // @Override | ||
475 | // public void updateTernaryEdge(boolean isInsertion, Object relation, Object from, Object to, Object typeObject) { | ||
476 | // Address<? extends Tunnel> root = inputConnector.getTernaryEdgeRoot(typeObject); | ||
477 | // if (root != null) { | ||
478 | // network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(relation), inputConnector.wrapElement(from), | ||
479 | // inputConnector.wrapElement(to))); | ||
480 | // if (!engine.isParallelExecutionEnabled()) | ||
481 | // network.waitForReteTermination(); | ||
482 | // } | ||
483 | // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) { | ||
484 | // for (Object superType : context.enumerateDirectTernaryEdgeSupertypes(typeObject)) { | ||
485 | // updateTernaryEdge(isInsertion, relation, from, to, superType); | ||
486 | // } | ||
487 | // } | ||
488 | // } | ||
489 | // @Override | ||
490 | // public void updateBinaryEdge(boolean isInsertion, Object from, Object to, Object typeObject) { | ||
491 | // Address<? extends Tunnel> root = inputConnector.getBinaryEdgeRoot(typeObject); | ||
492 | // if (root != null) { | ||
493 | // network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(from), inputConnector.wrapElement(to))); | ||
494 | // if (!engine.isParallelExecutionEnabled()) | ||
495 | // network.waitForReteTermination(); | ||
496 | // } | ||
497 | // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) { | ||
498 | // for (Object superType : context.enumerateDirectBinaryEdgeSupertypes(typeObject)) { | ||
499 | // updateBinaryEdge(isInsertion, from, to, superType); | ||
500 | // } | ||
501 | // } | ||
502 | // } | ||
503 | // | ||
504 | // @Override | ||
505 | // public void updateContainment(boolean isInsertion, Object container, Object element) { | ||
506 | // final Address<? extends Tunnel> containmentRoot = inputConnector.getContainmentRoot(); | ||
507 | // if (containmentRoot != null) { | ||
508 | // network.sendExternalUpdate(containmentRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(container), | ||
509 | // inputConnector.wrapElement(element))); | ||
510 | // if (!engine.isParallelExecutionEnabled()) | ||
511 | // network.waitForReteTermination(); | ||
512 | // } | ||
513 | // } | ||
514 | // | ||
515 | // @Override | ||
516 | // public void updateInstantiation(boolean isInsertion, Object parent, Object child) { | ||
517 | // final Address<? extends Tunnel> instantiationRoot = inputConnector.getInstantiationRoot(); | ||
518 | // if (instantiationRoot != null) { | ||
519 | // network.sendExternalUpdate(instantiationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent), | ||
520 | // inputConnector.wrapElement(child))); | ||
521 | // if (!engine.isParallelExecutionEnabled()) | ||
522 | // network.waitForReteTermination(); | ||
523 | // } | ||
524 | // } | ||
525 | // | ||
526 | // @Override | ||
527 | // public void updateGeneralization(boolean isInsertion, Object parent, Object child) { | ||
528 | // final Address<? extends Tunnel> generalizationRoot = inputConnector.getGeneralizationRoot(); | ||
529 | // if (generalizationRoot != null) { | ||
530 | // network.sendExternalUpdate(generalizationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent), | ||
531 | // inputConnector.wrapElement(child))); | ||
532 | // if (!engine.isParallelExecutionEnabled()) | ||
533 | // network.waitForReteTermination(); | ||
534 | // } | ||
535 | // } | ||
536 | |||
537 | // no wrapping needed! | ||
538 | public void notifyEvaluator(Address<? extends Receiver> receiver, Tuple tuple) { | ||
539 | network.sendExternalUpdate(receiver, Direction.INSERT, tuple); | ||
540 | if (!engine.isParallelExecutionEnabled()) | ||
541 | network.waitForReteTermination(); | ||
542 | } | ||
543 | |||
544 | public void mapPlanToAddress(SubPlan plan, Address<? extends Supplier> handle) { | ||
545 | subplanToAddressMapping.put(plan, handle); | ||
546 | } | ||
547 | |||
548 | public Address<? extends Supplier> getAddress(SubPlan plan) { | ||
549 | return subplanToAddressMapping.get(plan); | ||
550 | } | ||
551 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java new file mode 100644 index 00000000..bd1b219e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java | |||
@@ -0,0 +1,50 @@ | |||
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.rete.construction; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; | ||
13 | |||
14 | /** | ||
15 | * A problem has occurred during the construction of the RETE net. | ||
16 | * | ||
17 | * @author Gabor Bergmann | ||
18 | * | ||
19 | */ | ||
20 | public class RetePatternBuildException extends QueryProcessingException { | ||
21 | |||
22 | private static final long serialVersionUID = 6966585498204577548L; | ||
23 | |||
24 | /** | ||
25 | * @param message | ||
26 | * The template of the exception message | ||
27 | * @param context | ||
28 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
29 | * defined | ||
30 | * @param patternDescription | ||
31 | * the PatternDescription where the exception occurred | ||
32 | */ | ||
33 | public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription) { | ||
34 | super(message, context, shortMessage, patternDescription); | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * @param message | ||
39 | * The template of the exception message | ||
40 | * @param context | ||
41 | * The data elements to be used to instantiate the template. Can be null if no context parameter is | ||
42 | * defined | ||
43 | * @param patternDescription | ||
44 | * the PatternDescription where the exception occurred | ||
45 | */ | ||
46 | public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription, | ||
47 | Throwable cause) { | ||
48 | super(message, context, shortMessage, patternDescription, cause); | ||
49 | } | ||
50 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java new file mode 100644 index 00000000..bd22e1a0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java | |||
@@ -0,0 +1,171 @@ | |||
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.rete.construction.basiclinear; | ||
11 | |||
12 | import java.util.Arrays; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import org.apache.log4j.Logger; | ||
17 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
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.IQueryPlannerStrategy; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper; | ||
24 | import tools.refinery.viatra.runtime.matchers.planning.operations.PApply; | ||
25 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
26 | import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
36 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
37 | import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException; | ||
38 | |||
39 | /** | ||
40 | * Basic layout that builds a linear RETE net based on a heuristic ordering of constraints. | ||
41 | * | ||
42 | * @author Gabor Bergmann | ||
43 | * | ||
44 | */ | ||
45 | public class BasicLinearLayout implements IQueryPlannerStrategy { | ||
46 | |||
47 | //SubPlanProcessor planProcessor = new SubPlanProcessor(); | ||
48 | |||
49 | private IQueryBackendHintProvider hintProvider; | ||
50 | private IQueryBackendContext bContext; | ||
51 | /** | ||
52 | * @param bContext | ||
53 | * @since 1.5 | ||
54 | */ | ||
55 | public BasicLinearLayout(IQueryBackendContext bContext) { | ||
56 | this.bContext = bContext; | ||
57 | this.hintProvider = bContext.getHintProvider(); | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public SubPlan plan(final PBody pSystem, Logger logger, IQueryMetaContext context) { | ||
62 | SubPlanFactory planFactory = new SubPlanFactory(pSystem); | ||
63 | PQuery query = pSystem.getPattern(); | ||
64 | //planProcessor.setCompiler(compiler); | ||
65 | try { | ||
66 | logger.debug(String.format( | ||
67 | "%s: patternbody build started for %s", | ||
68 | getClass().getSimpleName(), | ||
69 | query.getFullyQualifiedName())); | ||
70 | |||
71 | // STARTING THE LINE | ||
72 | SubPlan plan = planFactory.createSubPlan(new PStart()); | ||
73 | |||
74 | Set<PConstraint> pQueue = CollectionsFactory.createSet(pSystem.getConstraints()); | ||
75 | |||
76 | // MAIN LOOP | ||
77 | while (!pQueue.isEmpty()) { | ||
78 | PConstraint pConstraint = Collections.min(pQueue, | ||
79 | new OrderingHeuristics(plan, context)); // pQueue.iterator().next(); | ||
80 | pQueue.remove(pConstraint); | ||
81 | |||
82 | // if we have no better option than an unready deferred constraint, raise error | ||
83 | if (pConstraint instanceof DeferredPConstraint) { | ||
84 | final DeferredPConstraint deferred = (DeferredPConstraint) pConstraint; | ||
85 | if (!deferred.isReadyAt(plan, context)) { | ||
86 | raiseForeverDeferredError(deferred, plan, context); | ||
87 | } | ||
88 | } | ||
89 | // TODO integrate the check above in SubPlan / POperation?? | ||
90 | |||
91 | // replace incumbent plan with its child | ||
92 | plan = planFactory.createSubPlan(new PApply(pConstraint), plan); | ||
93 | } | ||
94 | |||
95 | // PROJECT TO PARAMETERS | ||
96 | SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), plan); | ||
97 | |||
98 | // FINAL CHECK, whether all exported variables are present + all constraint satisfied | ||
99 | BuildHelper.finalCheck(pSystem, finalPlan, context); | ||
100 | // TODO integrate the check above in SubPlan / POperation | ||
101 | |||
102 | logger.debug(String.format( | ||
103 | "%s: patternbody query plan concluded for %s as: %s", | ||
104 | getClass().getSimpleName(), | ||
105 | query.getFullyQualifiedName(), | ||
106 | finalPlan.toLongString())); | ||
107 | |||
108 | return finalPlan; | ||
109 | |||
110 | } catch (RetePatternBuildException ex) { | ||
111 | ex.setPatternDescription(query); | ||
112 | throw ex; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * Called when the constraint is not ready, but cannot be deferred further. | ||
118 | * | ||
119 | * @param plan | ||
120 | * @throws RetePatternBuildException | ||
121 | * to indicate the error in detail. | ||
122 | */ | ||
123 | private void raiseForeverDeferredError(DeferredPConstraint constraint, SubPlan plan, IQueryMetaContext context) { | ||
124 | if (constraint instanceof Equality) { | ||
125 | raiseForeverDeferredError((Equality)constraint, plan, context); | ||
126 | } else if (constraint instanceof ExportedParameter) { | ||
127 | raiseForeverDeferredError((ExportedParameter)constraint, plan, context); | ||
128 | } else if (constraint instanceof ExpressionEvaluation) { | ||
129 | raiseForeverDeferredError((ExpressionEvaluation)constraint, plan, context); | ||
130 | } else if (constraint instanceof VariableDeferredPConstraint) { | ||
131 | raiseForeverDeferredError(constraint, plan, context); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | private void raiseForeverDeferredError(Equality constraint, SubPlan plan, IQueryMetaContext context) { | ||
136 | String[] args = { constraint.getWho().toString(), constraint.getWithWhom().toString() }; | ||
137 | String msg = "Cannot express equality of variables {1} and {2} if neither of them is deducable."; | ||
138 | String shortMsg = "Equality between undeducible variables."; | ||
139 | throw new RetePatternBuildException(msg, args, shortMsg, null); | ||
140 | } | ||
141 | private void raiseForeverDeferredError(ExportedParameter constraint, SubPlan plan, IQueryMetaContext context) { | ||
142 | String[] args = { constraint.getParameterName() }; | ||
143 | String msg = "Pattern Graph Search terminated incompletely: " | ||
144 | + "exported pattern variable {1} could not be determined based on the pattern constraints. " | ||
145 | + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters."; | ||
146 | String shortMsg = "Could not deduce value of parameter"; | ||
147 | throw new RetePatternBuildException(msg, args, shortMsg, null); | ||
148 | } | ||
149 | private void raiseForeverDeferredError(ExpressionEvaluation constraint, SubPlan plan, IQueryMetaContext context) { | ||
150 | if (constraint.checkTypeSafety(plan, context) == null) { | ||
151 | raiseForeverDeferredError(constraint, plan); | ||
152 | } else { | ||
153 | String[] args = { toString(), constraint.checkTypeSafety(plan, context).toString() }; | ||
154 | String msg = "The checking of pattern constraint {1} cannot be deferred further, but variable {2} is still not type safe. " | ||
155 | + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible."; | ||
156 | String shortMsg = "Could not check all constraints due to undeducible type restrictions"; | ||
157 | throw new RetePatternBuildException(msg, args, shortMsg, null); | ||
158 | } | ||
159 | } | ||
160 | private void raiseForeverDeferredError(VariableDeferredPConstraint constraint, SubPlan plan) { | ||
161 | Set<PVariable> missing = CollectionsFactory.createSet(constraint.getDeferringVariables());//new HashSet<PVariable>(getDeferringVariables()); | ||
162 | missing.removeAll(plan.getVisibleVariables()); | ||
163 | String[] args = { toString(), Arrays.toString(missing.toArray()) }; | ||
164 | String msg = "The checking of pattern constraint {1} requires the values of variables {2}, but it cannot be deferred further. " | ||
165 | + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible."; | ||
166 | String shortMsg = "Could not check all constraints due to undeducible variables"; | ||
167 | throw new RetePatternBuildException(msg, args, shortMsg, null); | ||
168 | } | ||
169 | |||
170 | |||
171 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java new file mode 100644 index 00000000..2b4e8890 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java | |||
@@ -0,0 +1,90 @@ | |||
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.rete.construction.basiclinear; | ||
11 | |||
12 | import java.util.Comparator; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
16 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
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.basicenumerables.ConstantValue; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent; | ||
24 | |||
25 | /** | ||
26 | * @author Gabor Bergmann | ||
27 | * | ||
28 | */ | ||
29 | public class OrderingHeuristics implements Comparator<PConstraint> { | ||
30 | private SubPlan plan; | ||
31 | private IQueryMetaContext context; | ||
32 | |||
33 | public OrderingHeuristics(SubPlan plan, IQueryMetaContext context) { | ||
34 | super(); | ||
35 | this.plan = plan; | ||
36 | this.context = context; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public int compare(PConstraint o1, PConstraint o2) { | ||
41 | return new OrderingCompareAgent<PConstraint>(o1, o2) { | ||
42 | @Override | ||
43 | protected void doCompare() { | ||
44 | boolean temp = consider(preferTrue(isConstant(a), isConstant(b))) | ||
45 | && consider(preferTrue(isReady(a), isReady(b))); | ||
46 | if (!temp) | ||
47 | return; | ||
48 | |||
49 | Set<PVariable> bound1 = boundVariables(a); | ||
50 | Set<PVariable> bound2 = boundVariables(b); | ||
51 | swallowBoolean(temp && consider(preferTrue(isBound(a, bound1), isBound(b, bound2))) | ||
52 | && consider(preferMore(degreeBound(a, bound1), degreeBound(b, bound2))) | ||
53 | && consider(preferLess(degreeFree(a, bound1), degreeFree(b, bound2))) | ||
54 | |||
55 | // tie breaking | ||
56 | && consider(preferLess(a.getMonotonousID(), b.getMonotonousID())) // this is hopefully deterministic | ||
57 | && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b)))); | ||
58 | } | ||
59 | }.compare(); | ||
60 | } | ||
61 | |||
62 | boolean isConstant(PConstraint o) { | ||
63 | return (o instanceof ConstantValue); | ||
64 | } | ||
65 | |||
66 | boolean isReady(PConstraint o) { | ||
67 | return (o instanceof EnumerablePConstraint) | ||
68 | || (o instanceof DeferredPConstraint && ((DeferredPConstraint) o) | ||
69 | .isReadyAt(plan, context)); | ||
70 | } | ||
71 | |||
72 | Set<PVariable> boundVariables(PConstraint o) { | ||
73 | Set<PVariable> boundVariables = CollectionsFactory.createSet(o.getAffectedVariables()); | ||
74 | boundVariables.retainAll(plan.getVisibleVariables()); | ||
75 | return boundVariables; | ||
76 | } | ||
77 | |||
78 | boolean isBound(PConstraint o, Set<PVariable> boundVariables) { | ||
79 | return boundVariables.size() == o.getAffectedVariables().size(); | ||
80 | } | ||
81 | |||
82 | int degreeBound(PConstraint o, Set<PVariable> boundVariables) { | ||
83 | return boundVariables.size(); | ||
84 | } | ||
85 | |||
86 | int degreeFree(PConstraint o, Set<PVariable> boundVariables) { | ||
87 | return o.getAffectedVariables().size() - boundVariables.size(); | ||
88 | } | ||
89 | |||
90 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java new file mode 100644 index 00000000..da2fb432 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java | |||
@@ -0,0 +1,390 @@ | |||
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.rete.construction.plancompiler; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.LinkedHashSet; | ||
16 | import java.util.LinkedList; | ||
17 | import java.util.List; | ||
18 | import java.util.Map; | ||
19 | import java.util.Map.Entry; | ||
20 | import java.util.Set; | ||
21 | import java.util.SortedSet; | ||
22 | import java.util.TreeSet; | ||
23 | |||
24 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
25 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
26 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
27 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
29 | import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
35 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
36 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
37 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; | ||
38 | import tools.refinery.viatra.runtime.rete.recipes.EqualityFilterRecipe; | ||
39 | import tools.refinery.viatra.runtime.rete.recipes.IndexerBasedAggregatorRecipe; | ||
40 | import tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe; | ||
41 | import tools.refinery.viatra.runtime.rete.recipes.JoinRecipe; | ||
42 | import tools.refinery.viatra.runtime.rete.recipes.Mask; | ||
43 | import tools.refinery.viatra.runtime.rete.recipes.MonotonicityInfo; | ||
44 | import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe; | ||
45 | import tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe; | ||
46 | import tools.refinery.viatra.runtime.rete.recipes.RecipesFactory; | ||
47 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
48 | import tools.refinery.viatra.runtime.rete.recipes.SingleColumnAggregatorRecipe; | ||
49 | import tools.refinery.viatra.runtime.rete.recipes.TrimmerRecipe; | ||
50 | import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; | ||
51 | import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; | ||
52 | import tools.refinery.viatra.runtime.rete.traceability.CompiledSubPlan; | ||
53 | import tools.refinery.viatra.runtime.rete.traceability.PlanningTrace; | ||
54 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
55 | import tools.refinery.viatra.runtime.rete.util.ReteHintOptions; | ||
56 | |||
57 | /** | ||
58 | * @author Bergmann Gabor | ||
59 | * | ||
60 | */ | ||
61 | public class CompilerHelper { | ||
62 | |||
63 | private CompilerHelper() {/*Utility class constructor*/} | ||
64 | |||
65 | static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE; | ||
66 | |||
67 | /** | ||
68 | * Makes sure that all variables in the tuple are different so that it can be used as {@link CompiledSubPlan}. If a | ||
69 | * variable occurs multiple times, equality checks are applied and then the results are trimmed so that duplicates | ||
70 | * are hidden. If no manipulation is necessary, the original trace is returned. | ||
71 | * | ||
72 | * <p> | ||
73 | * to be used whenever a constraint introduces new variables. | ||
74 | */ | ||
75 | public static PlanningTrace checkAndTrimEqualVariables(SubPlan plan, final PlanningTrace coreTrace) { | ||
76 | // are variables in the constraint all different? | ||
77 | final List<PVariable> coreVariablesTuple = coreTrace.getVariablesTuple(); | ||
78 | final int constraintArity = coreVariablesTuple.size(); | ||
79 | final int distinctVariables = coreTrace.getPosMapping().size(); | ||
80 | if (constraintArity == distinctVariables) { | ||
81 | // all variables occur exactly once in tuple | ||
82 | return coreTrace; | ||
83 | } else { // apply equality checks and trim | ||
84 | |||
85 | // find the positions in the tuple for each variable | ||
86 | Map<PVariable, SortedSet<Integer>> posMultimap = new HashMap<PVariable, SortedSet<Integer>>(); | ||
87 | List<PVariable> trimmedVariablesTuple = new ArrayList<PVariable>(distinctVariables); | ||
88 | int[] trimIndices = new int[distinctVariables]; | ||
89 | for (int i = 0; i < constraintArity; ++i) { | ||
90 | final PVariable variable = coreVariablesTuple.get(i); | ||
91 | SortedSet<Integer> indexSet = posMultimap.get(variable); | ||
92 | if (indexSet == null) { // first occurrence of variable | ||
93 | indexSet = new TreeSet<Integer>(); | ||
94 | posMultimap.put(variable, indexSet); | ||
95 | |||
96 | // this is the first occurrence, set up trimming | ||
97 | trimIndices[trimmedVariablesTuple.size()] = i; | ||
98 | trimmedVariablesTuple.add(variable); | ||
99 | } | ||
100 | indexSet.add(i); | ||
101 | } | ||
102 | |||
103 | // construct equality checks for each variable occurring multiple times | ||
104 | PlanningTrace lastTrace = coreTrace; | ||
105 | for (Entry<PVariable, SortedSet<Integer>> entry : posMultimap.entrySet()) { | ||
106 | if (entry.getValue().size() > 1) { | ||
107 | EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe(); | ||
108 | equalityFilterRecipe.setParent(lastTrace.getRecipe()); | ||
109 | equalityFilterRecipe.getIndices().addAll(entry.getValue()); | ||
110 | lastTrace = new PlanningTrace(plan, coreVariablesTuple, equalityFilterRecipe, lastTrace); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | // trim so that each variable occurs only once | ||
115 | TrimmerRecipe trimmerRecipe = FACTORY.createTrimmerRecipe(); | ||
116 | trimmerRecipe.setParent(lastTrace.getRecipe()); | ||
117 | trimmerRecipe.setMask(tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper | ||
118 | .mask(constraintArity, trimIndices)); | ||
119 | return new PlanningTrace(plan, trimmedVariablesTuple, trimmerRecipe, lastTrace); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Extracts the variable list representation of the variables tuple. | ||
125 | */ | ||
126 | public static List<PVariable> convertVariablesTuple(EnumerablePConstraint constraint) { | ||
127 | return convertVariablesTuple(constraint.getVariablesTuple()); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Extracts the variable list representation of the variables tuple. | ||
132 | */ | ||
133 | public static List<PVariable> convertVariablesTuple(Tuple variablesTuple) { | ||
134 | List<PVariable> result = new ArrayList<PVariable>(); | ||
135 | for (Object o : variablesTuple.getElements()) | ||
136 | result.add((PVariable) o); | ||
137 | return result; | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * Returns a compiled indexer trace according to a mask | ||
142 | */ | ||
143 | public static RecipeTraceInfo makeIndexerTrace(SubPlan planToCompile, PlanningTrace parentTrace, TupleMask mask) { | ||
144 | final ReteNodeRecipe parentRecipe = parentTrace.getRecipe(); | ||
145 | if (parentRecipe instanceof IndexerBasedAggregatorRecipe | ||
146 | || parentRecipe instanceof SingleColumnAggregatorRecipe) | ||
147 | throw new IllegalArgumentException( | ||
148 | "Cannot take projection indexer of aggregator node at plan " + planToCompile); | ||
149 | IndexerRecipe recipe = RecipesHelper.projectionIndexerRecipe(parentRecipe, toRecipeMask(mask)); | ||
150 | // final List<PVariable> maskedVariables = mask.transform(parentTrace.getVariablesTuple()); | ||
151 | return new PlanningTrace(planToCompile, /* maskedVariables */ parentTrace.getVariablesTuple(), recipe, | ||
152 | parentTrace); | ||
153 | // TODO add specialized indexer trace info? | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * Creates a trimmer that keeps selected variables only. | ||
158 | */ | ||
159 | protected static TrimmerRecipe makeTrimmerRecipe(final PlanningTrace compiledParent, | ||
160 | List<PVariable> projectedVariables) { | ||
161 | final Mask projectionMask = makeProjectionMask(compiledParent, projectedVariables); | ||
162 | final TrimmerRecipe trimmerRecipe = ReteRecipeCompiler.FACTORY.createTrimmerRecipe(); | ||
163 | trimmerRecipe.setParent(compiledParent.getRecipe()); | ||
164 | trimmerRecipe.setMask(projectionMask); | ||
165 | return trimmerRecipe; | ||
166 | } | ||
167 | |||
168 | public static Mask makeProjectionMask(final PlanningTrace compiledParent, Iterable<PVariable> projectedVariables) { | ||
169 | List<Integer> projectionSourceIndices = new ArrayList<Integer>(); | ||
170 | for (PVariable pVariable : projectedVariables) { | ||
171 | projectionSourceIndices.add(compiledParent.getPosMapping().get(pVariable)); | ||
172 | } | ||
173 | final Mask projectionMask = RecipesHelper.mask(compiledParent.getRecipe().getArity(), projectionSourceIndices); | ||
174 | return projectionMask; | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * @since 1.6 | ||
179 | */ | ||
180 | public static final class PosetTriplet { | ||
181 | public Mask coreMask; | ||
182 | public Mask posetMask; | ||
183 | public IPosetComparator comparator; | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @since 1.6 | ||
188 | */ | ||
189 | public static PosetTriplet computePosetInfo(List<PVariable> variables, PBody body, IQueryMetaContext context) { | ||
190 | Map<PVariable, Set<IInputKey>> typeMap = TypeHelper.inferUnaryTypesFor(variables, body.getConstraints(), | ||
191 | context); | ||
192 | List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>(); | ||
193 | |||
194 | for (int i = 0; i < variables.size(); i++) { | ||
195 | keys.add(typeMap.get(variables.get(i))); | ||
196 | } | ||
197 | |||
198 | return computePosetInfo(keys, context); | ||
199 | } | ||
200 | |||
201 | /** | ||
202 | * @since 1.6 | ||
203 | */ | ||
204 | public static PosetTriplet computePosetInfo(List<PParameter> parameters, IQueryMetaContext context) { | ||
205 | List<Set<IInputKey>> keys = new LinkedList<Set<IInputKey>>(); | ||
206 | for (int i = 0; i < parameters.size(); i++) { | ||
207 | IInputKey key = parameters.get(i).getDeclaredUnaryType(); | ||
208 | if (key == null) { | ||
209 | keys.add(Collections.emptySet()); | ||
210 | } else { | ||
211 | keys.add(Collections.singleton(parameters.get(i).getDeclaredUnaryType())); | ||
212 | } | ||
213 | } | ||
214 | return computePosetInfo(keys, context); | ||
215 | } | ||
216 | |||
217 | |||
218 | |||
219 | /** | ||
220 | * @since 1.6 | ||
221 | */ | ||
222 | public static PosetTriplet computePosetInfo(Iterable<Set<IInputKey>> keys, IQueryMetaContext context) { | ||
223 | PosetTriplet result = new PosetTriplet(); | ||
224 | List<Integer> coreIndices = new ArrayList<Integer>(); | ||
225 | List<Integer> posetIndices = new ArrayList<Integer>(); | ||
226 | List<IInputKey> filtered = new ArrayList<IInputKey>(); | ||
227 | boolean posetKey = false; | ||
228 | int index = -1; | ||
229 | |||
230 | for (Set<IInputKey> _keys : keys) { | ||
231 | ++index; | ||
232 | posetKey = false; | ||
233 | |||
234 | for (IInputKey key : _keys) { | ||
235 | if (key != null && context.isPosetKey(key)) { | ||
236 | posetKey = true; | ||
237 | filtered.add(key); | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | if (posetKey) { | ||
243 | posetIndices.add(index); | ||
244 | } else { | ||
245 | coreIndices.add(index); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | result.comparator = context.getPosetComparator(filtered); | ||
250 | result.coreMask = RecipesHelper.mask(index + 1, coreIndices); | ||
251 | result.posetMask = RecipesHelper.mask(index + 1, posetIndices); | ||
252 | |||
253 | return result; | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * Creates a recipe for a production node and the corresponding trace. | ||
258 | * <p> PRE: in case this is a recursion cutoff point (see {@link RecursionCutoffPoint}) | ||
259 | * and bodyFinalTraces will be filled later, | ||
260 | * the object yielded now by bodyFinalTraces.values() must return up-to-date results later | ||
261 | * @since 2.4 | ||
262 | */ | ||
263 | public static CompiledQuery makeQueryTrace(PQuery query, Map<PBody, RecipeTraceInfo> bodyFinalTraces, | ||
264 | Collection<ReteNodeRecipe> bodyFinalRecipes, QueryEvaluationHint hint, IQueryMetaContext context, | ||
265 | boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { | ||
266 | ProductionRecipe recipe = ReteRecipeCompiler.FACTORY.createProductionRecipe(); | ||
267 | |||
268 | // temporary solution to support the deprecated option for now | ||
269 | boolean deleteAndRederiveEvaluationDep = deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(hint); | ||
270 | |||
271 | recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); | ||
272 | |||
273 | if (deleteAndRederiveEvaluationDep || (timelyEvaluation != null)) { | ||
274 | PosetTriplet triplet = computePosetInfo(query.getParameters(), context); | ||
275 | if (triplet.comparator != null) { | ||
276 | MonotonicityInfo info = FACTORY.createMonotonicityInfo(); | ||
277 | info.setCoreMask(triplet.coreMask); | ||
278 | info.setPosetMask(triplet.posetMask); | ||
279 | info.setPosetComparator(triplet.comparator); | ||
280 | recipe.setOptionalMonotonicityInfo(info); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | recipe.setPattern(query); | ||
285 | recipe.setPatternFQN(query.getFullyQualifiedName()); | ||
286 | recipe.setTraceInfo(recipe.getPatternFQN()); | ||
287 | recipe.getParents().addAll(bodyFinalRecipes); | ||
288 | for (int i = 0; i < query.getParameterNames().size(); ++i) { | ||
289 | recipe.getMappedIndices().put(query.getParameterNames().get(i), i); | ||
290 | } | ||
291 | |||
292 | return new CompiledQuery(recipe, bodyFinalTraces, query); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * Calculated index mappings for a join, based on the common variables of the two parent subplans. | ||
297 | * | ||
298 | * @author Gabor Bergmann | ||
299 | * | ||
300 | */ | ||
301 | public static class JoinHelper { | ||
302 | private TupleMask primaryMask; | ||
303 | private TupleMask secondaryMask; | ||
304 | private TupleMask complementerMask; | ||
305 | private RecipeTraceInfo primaryIndexer; | ||
306 | private RecipeTraceInfo secondaryIndexer; | ||
307 | private JoinRecipe naturalJoinRecipe; | ||
308 | private List<PVariable> naturalJoinVariablesTuple; | ||
309 | |||
310 | /** | ||
311 | * @pre enforceVariableCoincidences() has been called on both sides. | ||
312 | */ | ||
313 | public JoinHelper(SubPlan planToCompile, PlanningTrace primaryCompiled, PlanningTrace callTrace) { | ||
314 | super(); | ||
315 | |||
316 | Set<PVariable> primaryVariables = new LinkedHashSet<PVariable>(primaryCompiled.getVariablesTuple()); | ||
317 | Set<PVariable> secondaryVariables = new LinkedHashSet<PVariable>(callTrace.getVariablesTuple()); | ||
318 | int oldNodes = 0; | ||
319 | Set<Integer> introducingSecondaryIndices = new TreeSet<Integer>(); | ||
320 | for (PVariable var : secondaryVariables) { | ||
321 | if (primaryVariables.contains(var)) | ||
322 | oldNodes++; | ||
323 | else | ||
324 | introducingSecondaryIndices.add(callTrace.getPosMapping().get(var)); | ||
325 | } | ||
326 | List<Integer> primaryIndices = new ArrayList<Integer>(oldNodes); | ||
327 | List<Integer> secondaryIndices = new ArrayList<Integer>(oldNodes); | ||
328 | for (PVariable var : secondaryVariables) { | ||
329 | if (primaryVariables.contains(var)) { | ||
330 | primaryIndices.add(primaryCompiled.getPosMapping().get(var)); | ||
331 | secondaryIndices.add(callTrace.getPosMapping().get(var)); | ||
332 | } | ||
333 | } | ||
334 | Collection<Integer> complementerIndices = introducingSecondaryIndices; | ||
335 | |||
336 | primaryMask = TupleMask.fromSelectedIndices(primaryCompiled.getVariablesTuple().size(), primaryIndices); | ||
337 | secondaryMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), secondaryIndices); | ||
338 | complementerMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), complementerIndices); | ||
339 | |||
340 | primaryIndexer = makeIndexerTrace(planToCompile, primaryCompiled, primaryMask); | ||
341 | secondaryIndexer = makeIndexerTrace(planToCompile, callTrace, secondaryMask); | ||
342 | |||
343 | naturalJoinRecipe = FACTORY.createJoinRecipe(); | ||
344 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | ||
345 | naturalJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe()); | ||
346 | naturalJoinRecipe.setRightParentComplementaryMask(CompilerHelper.toRecipeMask(complementerMask)); | ||
347 | |||
348 | naturalJoinVariablesTuple = new ArrayList<PVariable>(primaryCompiled.getVariablesTuple()); | ||
349 | for (int complementerIndex : complementerMask.indices) | ||
350 | naturalJoinVariablesTuple.add(callTrace.getVariablesTuple().get(complementerIndex)); | ||
351 | } | ||
352 | |||
353 | public TupleMask getPrimaryMask() { | ||
354 | return primaryMask; | ||
355 | } | ||
356 | |||
357 | public TupleMask getSecondaryMask() { | ||
358 | return secondaryMask; | ||
359 | } | ||
360 | |||
361 | public TupleMask getComplementerMask() { | ||
362 | return complementerMask; | ||
363 | } | ||
364 | |||
365 | public RecipeTraceInfo getPrimaryIndexer() { | ||
366 | return primaryIndexer; | ||
367 | } | ||
368 | |||
369 | public RecipeTraceInfo getSecondaryIndexer() { | ||
370 | return secondaryIndexer; | ||
371 | } | ||
372 | |||
373 | public JoinRecipe getNaturalJoinRecipe() { | ||
374 | return naturalJoinRecipe; | ||
375 | } | ||
376 | |||
377 | public List<PVariable> getNaturalJoinVariablesTuple() { | ||
378 | return naturalJoinVariablesTuple; | ||
379 | } | ||
380 | |||
381 | } | ||
382 | |||
383 | /** | ||
384 | * @since 1.4 | ||
385 | */ | ||
386 | public static Mask toRecipeMask(TupleMask mask) { | ||
387 | return RecipesHelper.mask(mask.sourceWidth, mask.indices); | ||
388 | } | ||
389 | |||
390 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java new file mode 100644 index 00000000..7d1e4d3a --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java | |||
@@ -0,0 +1,86 @@ | |||
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.rete.construction.plancompiler; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
21 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; | ||
22 | import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe; | ||
23 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
24 | import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; | ||
25 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
26 | |||
27 | /** | ||
28 | * In a recursive query structure, query composition references can be cut off so that the remaining structure is DAG. | ||
29 | * {@link RecursionCutoffPoint} represents one such cut off query composition. | ||
30 | * When the compilation of the recursive query finishes and the compiled form becomes available, | ||
31 | * the {@link RecursionCutoffPoint} has to be signaled to update parent traces and recipes of the recursive call. | ||
32 | * | ||
33 | * @author Bergmann Gabor | ||
34 | * @noreference This class is not intended to be referenced by clients | ||
35 | * | ||
36 | */ | ||
37 | public class RecursionCutoffPoint { | ||
38 | final Map<PBody, RecipeTraceInfo> futureTraceMap; | ||
39 | final CompiledQuery compiledQuery; | ||
40 | final ProductionRecipe recipe; | ||
41 | final QueryEvaluationHint hint; | ||
42 | |||
43 | public RecursionCutoffPoint(PQuery query, QueryEvaluationHint hint, IQueryMetaContext context, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { | ||
44 | super(); | ||
45 | this.hint = hint; | ||
46 | this.futureTraceMap = new HashMap<>(); // IMPORTANT: the identity of futureTraceMap.values() will not change | ||
47 | this.compiledQuery = CompilerHelper.makeQueryTrace(query, futureTraceMap, Collections.<ReteNodeRecipe>emptySet(), hint, context, deleteAndRederiveEvaluation, timelyEvaluation); | ||
48 | this.recipe = (ProductionRecipe)compiledQuery.getRecipe(); | ||
49 | if (!compiledQuery.getParentRecipeTraces().isEmpty()) { | ||
50 | throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has trace parents: %s", | ||
51 | compiledQuery.getQuery(), | ||
52 | prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces()))); | ||
53 | } | ||
54 | if (!recipe.getParents().isEmpty()) { | ||
55 | throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has recipe parents: %s", | ||
56 | compiledQuery.getQuery(), | ||
57 | prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces()))); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | |||
62 | |||
63 | private String prettyPrintParentRecipeTraces(List<RecipeTraceInfo> trace) { | ||
64 | return trace.stream().map(Object::toString).collect(Collectors.joining(", ")); | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * Signals that compilation of the recursive query has terminated, culminating into the given compiled form. | ||
69 | * The query composition that has been cut off will be connected now. | ||
70 | */ | ||
71 | public void mend(CompiledQuery finalCompiledForm) { | ||
72 | futureTraceMap.putAll(finalCompiledForm.getParentRecipeTracesPerBody()); | ||
73 | recipe.getParents().addAll(((ProductionRecipe)finalCompiledForm.getRecipe()).getParents()); | ||
74 | } | ||
75 | |||
76 | public CompiledQuery getCompiledQuery() { | ||
77 | return compiledQuery; | ||
78 | } | ||
79 | |||
80 | public ProductionRecipe getRecipe() { | ||
81 | return recipe; | ||
82 | } | ||
83 | |||
84 | |||
85 | |||
86 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java new file mode 100644 index 00000000..5df3a971 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java | |||
@@ -0,0 +1,949 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | package tools.refinery.viatra.runtime.rete.construction.plancompiler; | ||
11 | |||
12 | import org.apache.log4j.Logger; | ||
13 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
14 | import tools.refinery.viatra.runtime.matchers.backend.CommonQueryHintOptions; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
16 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IInputKey; | ||
18 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
19 | import tools.refinery.viatra.runtime.matchers.context.IQueryCacheContext; | ||
20 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
21 | import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy; | ||
22 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper; | ||
24 | import tools.refinery.viatra.runtime.matchers.planning.operations.*; | ||
25 | import tools.refinery.viatra.runtime.matchers.psystem.*; | ||
26 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
28 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; | ||
29 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*; | ||
30 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; | ||
31 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.rewriters.*; | ||
34 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
35 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
36 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
37 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
38 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; | ||
39 | import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; | ||
40 | import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.JoinHelper; | ||
41 | import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.PosetTriplet; | ||
42 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; | ||
43 | import tools.refinery.viatra.runtime.rete.recipes.*; | ||
44 | import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; | ||
45 | import tools.refinery.viatra.runtime.rete.traceability.*; | ||
46 | import tools.refinery.viatra.runtime.rete.util.ReteHintOptions; | ||
47 | |||
48 | import java.util.*; | ||
49 | import java.util.Map.Entry; | ||
50 | import java.util.function.BiFunction; | ||
51 | import java.util.function.Function; | ||
52 | |||
53 | /** | ||
54 | * Compiles queries and query plans into Rete recipes, traced by respectively a {@link CompiledQuery} or | ||
55 | * {@link CompiledSubPlan}. | ||
56 | * | ||
57 | * @author Bergmann Gabor | ||
58 | * | ||
59 | */ | ||
60 | public class ReteRecipeCompiler { | ||
61 | |||
62 | private final IQueryPlannerStrategy plannerStrategy; | ||
63 | private final IQueryMetaContext metaContext; | ||
64 | private final IQueryBackendHintProvider hintProvider; | ||
65 | private final PDisjunctionRewriter normalizer; | ||
66 | private final QueryAnalyzer queryAnalyzer; | ||
67 | private final Logger logger; | ||
68 | |||
69 | /** | ||
70 | * @since 2.2 | ||
71 | */ | ||
72 | protected final boolean deleteAndRederiveEvaluation; | ||
73 | /** | ||
74 | * @since 2.4 | ||
75 | */ | ||
76 | protected final TimelyConfiguration timelyEvaluation; | ||
77 | |||
78 | /** | ||
79 | * @since 1.5 | ||
80 | */ | ||
81 | public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, | ||
82 | IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer) { | ||
83 | this(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer, false, null); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * @since 2.4 | ||
88 | */ | ||
89 | public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, | ||
90 | IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer, | ||
91 | boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { | ||
92 | super(); | ||
93 | this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation; | ||
94 | this.timelyEvaluation = timelyEvaluation; | ||
95 | this.plannerStrategy = plannerStrategy; | ||
96 | this.logger = logger; | ||
97 | this.metaContext = metaContext; | ||
98 | this.queryAnalyzer = queryAnalyzer; | ||
99 | this.normalizer = new PDisjunctionRewriterCacher(new SurrogateQueryRewriter(), | ||
100 | new PBodyNormalizer(metaContext) { | ||
101 | |||
102 | @Override | ||
103 | protected boolean shouldExpandWeakenedAlternatives(PQuery query) { | ||
104 | QueryEvaluationHint hint = ReteRecipeCompiler.this.hintProvider.getQueryEvaluationHint(query); | ||
105 | Boolean expandWeakenedAlternativeConstraints = ReteHintOptions.expandWeakenedAlternativeConstraints | ||
106 | .getValueOrDefault(hint); | ||
107 | return expandWeakenedAlternativeConstraints; | ||
108 | } | ||
109 | |||
110 | }); | ||
111 | this.hintProvider = hintProvider; | ||
112 | } | ||
113 | |||
114 | static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE; | ||
115 | |||
116 | // INTERNALLY CACHED | ||
117 | private Map<PBody, SubPlan> plannerCache = new HashMap<PBody, SubPlan>(); | ||
118 | private Set<PBody> planningInProgress = new HashSet<PBody>(); | ||
119 | |||
120 | private Map<PQuery, CompiledQuery> queryCompilerCache = new HashMap<PQuery, CompiledQuery>(); | ||
121 | private Set<PQuery> compilationInProgress = new HashSet<PQuery>(); | ||
122 | private IMultiLookup<PQuery, RecursionCutoffPoint> recursionCutoffPoints = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); | ||
123 | private Map<SubPlan, CompiledSubPlan> subPlanCompilerCache = new HashMap<SubPlan, CompiledSubPlan>(); | ||
124 | private Map<ReteNodeRecipe, SubPlan> compilerBackTrace = new HashMap<ReteNodeRecipe, SubPlan>(); | ||
125 | |||
126 | /** | ||
127 | * Clears internal state | ||
128 | */ | ||
129 | public void reset() { | ||
130 | plannerCache.clear(); | ||
131 | planningInProgress.clear(); | ||
132 | queryCompilerCache.clear(); | ||
133 | subPlanCompilerCache.clear(); | ||
134 | compilerBackTrace.clear(); | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * Returns a {@link CompiledQuery} compiled from a query | ||
139 | * @throws ViatraQueryRuntimeException | ||
140 | */ | ||
141 | public CompiledQuery getCompiledForm(PQuery query) { | ||
142 | CompiledQuery compiled = queryCompilerCache.get(query); | ||
143 | if (compiled == null) { | ||
144 | |||
145 | IRewriterTraceCollector traceCollector = CommonQueryHintOptions.normalizationTraceCollector | ||
146 | .getValueOrDefault(hintProvider.getQueryEvaluationHint(query)); | ||
147 | if (traceCollector != null) { | ||
148 | traceCollector.addTrace(query, query); | ||
149 | } | ||
150 | |||
151 | boolean reentrant = !compilationInProgress.add(query); | ||
152 | if (reentrant) { // oops, recursion into body in progress | ||
153 | RecursionCutoffPoint cutoffPoint = new RecursionCutoffPoint(query, getHints(query), metaContext, | ||
154 | deleteAndRederiveEvaluation, timelyEvaluation); | ||
155 | recursionCutoffPoints.addPair(query, cutoffPoint); | ||
156 | return cutoffPoint.getCompiledQuery(); | ||
157 | } else { // not reentrant, therefore no recursion, do the compilation | ||
158 | try { | ||
159 | compiled = compileProduction(query); | ||
160 | queryCompilerCache.put(query, compiled); | ||
161 | // backTrace.put(compiled.getRecipe(), plan); | ||
162 | |||
163 | // if this was a recursive query, mend all points where recursion was cut off | ||
164 | for (RecursionCutoffPoint cutoffPoint : recursionCutoffPoints.lookupOrEmpty(query)) { | ||
165 | cutoffPoint.mend(compiled); | ||
166 | } | ||
167 | } finally { | ||
168 | compilationInProgress.remove(query); | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | return compiled; | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * Returns a {@link CompiledSubPlan} compiled from a query plan | ||
177 | * @throws ViatraQueryRuntimeException | ||
178 | */ | ||
179 | public CompiledSubPlan getCompiledForm(SubPlan plan) { | ||
180 | CompiledSubPlan compiled = subPlanCompilerCache.get(plan); | ||
181 | if (compiled == null) { | ||
182 | compiled = doCompileDispatch(plan); | ||
183 | subPlanCompilerCache.put(plan, compiled); | ||
184 | compilerBackTrace.put(compiled.getRecipe(), plan); | ||
185 | } | ||
186 | return compiled; | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * @throws ViatraQueryRuntimeException | ||
191 | */ | ||
192 | public SubPlan getPlan(PBody pBody) { | ||
193 | // if the query is not marked as being compiled, initiate compilation | ||
194 | // (this is useful in case of recursion if getPlan() is the entry point) | ||
195 | PQuery pQuery = pBody.getPattern(); | ||
196 | if (!compilationInProgress.contains(pQuery)) | ||
197 | getCompiledForm(pQuery); | ||
198 | |||
199 | // Is the plan already cached? | ||
200 | SubPlan plan = plannerCache.get(pBody); | ||
201 | if (plan == null) { | ||
202 | boolean reentrant = !planningInProgress.add(pBody); | ||
203 | if (reentrant) { // oops, recursion into body in progress | ||
204 | throw new IllegalArgumentException( | ||
205 | "Planning-level recursion unsupported: " + pBody.getPattern().getFullyQualifiedName()); | ||
206 | } else { // not reentrant, therefore no recursion, do the planning | ||
207 | try { | ||
208 | plan = plannerStrategy.plan(pBody, logger, metaContext); | ||
209 | plannerCache.put(pBody, plan); | ||
210 | } finally { | ||
211 | planningInProgress.remove(pBody); | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | return plan; | ||
216 | } | ||
217 | |||
218 | private CompiledQuery compileProduction(PQuery query) { | ||
219 | Collection<SubPlan> bodyPlans = new ArrayList<SubPlan>(); | ||
220 | normalizer.setTraceCollector(CommonQueryHintOptions.normalizationTraceCollector | ||
221 | .getValueOrDefault(hintProvider.getQueryEvaluationHint(query))); | ||
222 | for (PBody pBody : normalizer.rewrite(query).getBodies()) { | ||
223 | SubPlan bodyPlan = getPlan(pBody); | ||
224 | bodyPlans.add(bodyPlan); | ||
225 | } | ||
226 | return doCompileProduction(query, bodyPlans); | ||
227 | } | ||
228 | |||
229 | private CompiledQuery doCompileProduction(PQuery query, Collection<SubPlan> bodies) { | ||
230 | // TODO skip production node if there is just one body and no projection needed? | ||
231 | Map<PBody, RecipeTraceInfo> bodyFinalTraces = new HashMap<PBody, RecipeTraceInfo>(); | ||
232 | Collection<ReteNodeRecipe> bodyFinalRecipes = new HashSet<ReteNodeRecipe>(); | ||
233 | |||
234 | for (SubPlan bodyFinalPlan : bodies) { | ||
235 | // skip over any projections at the end | ||
236 | bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); | ||
237 | |||
238 | // TODO checkAndTrimEqualVariables may introduce superfluous trim, | ||
239 | // but whatever (no uniqueness enforcer needed) | ||
240 | |||
241 | // compile body | ||
242 | final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); | ||
243 | |||
244 | // project to parameter list | ||
245 | RecipeTraceInfo finalTrace = projectBodyFinalToParameters(compiledBody, false); | ||
246 | |||
247 | bodyFinalTraces.put(bodyFinalPlan.getBody(), finalTrace); | ||
248 | bodyFinalRecipes.add(finalTrace.getRecipe()); | ||
249 | } | ||
250 | |||
251 | CompiledQuery compiled = CompilerHelper.makeQueryTrace(query, bodyFinalTraces, bodyFinalRecipes, | ||
252 | getHints(query), metaContext, deleteAndRederiveEvaluation, timelyEvaluation); | ||
253 | |||
254 | return compiled; | ||
255 | } | ||
256 | |||
257 | private CompiledSubPlan doCompileDispatch(SubPlan plan) { | ||
258 | final POperation operation = plan.getOperation(); | ||
259 | if (operation instanceof PEnumerate) { | ||
260 | return doCompileEnumerate(((PEnumerate) operation).getEnumerablePConstraint(), plan); | ||
261 | } else if (operation instanceof PApply) { | ||
262 | final PConstraint pConstraint = ((PApply) operation).getPConstraint(); | ||
263 | if (pConstraint instanceof EnumerablePConstraint) { | ||
264 | CompiledSubPlan primaryParent = getCompiledForm(plan.getParentPlans().get(0)); | ||
265 | PlanningTrace secondaryParent = doEnumerateDispatch(plan, (EnumerablePConstraint) pConstraint); | ||
266 | return compileToNaturalJoin(plan, primaryParent, secondaryParent); | ||
267 | } else if (pConstraint instanceof DeferredPConstraint) { | ||
268 | return doDeferredDispatch((DeferredPConstraint) pConstraint, plan); | ||
269 | } else { | ||
270 | throw new IllegalArgumentException("Unsupported PConstraint in query plan: " + plan.toShortString()); | ||
271 | } | ||
272 | } else if (operation instanceof PJoin) { | ||
273 | return doCompileJoin((PJoin) operation, plan); | ||
274 | } else if (operation instanceof PProject) { | ||
275 | return doCompileProject((PProject) operation, plan); | ||
276 | } else if (operation instanceof PStart) { | ||
277 | return doCompileStart((PStart) operation, plan); | ||
278 | } else { | ||
279 | throw new IllegalArgumentException("Unsupported POperation in query plan: " + plan.toShortString()); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | private CompiledSubPlan doDeferredDispatch(DeferredPConstraint constraint, SubPlan plan) { | ||
284 | final SubPlan parentPlan = plan.getParentPlans().get(0); | ||
285 | final CompiledSubPlan parentCompiled = getCompiledForm(parentPlan); | ||
286 | if (constraint instanceof Equality) { | ||
287 | return compileDeferred((Equality) constraint, plan, parentPlan, parentCompiled); | ||
288 | } else if (constraint instanceof ExportedParameter) { | ||
289 | return compileDeferred((ExportedParameter) constraint, plan, parentPlan, parentCompiled); | ||
290 | } else if (constraint instanceof Inequality) { | ||
291 | return compileDeferred((Inequality) constraint, plan, parentPlan, parentCompiled); | ||
292 | } else if (constraint instanceof NegativePatternCall) { | ||
293 | return compileDeferred((NegativePatternCall) constraint, plan, parentPlan, parentCompiled); | ||
294 | } else if (constraint instanceof PatternMatchCounter) { | ||
295 | return compileDeferred((PatternMatchCounter) constraint, plan, parentPlan, parentCompiled); | ||
296 | } else if (constraint instanceof AggregatorConstraint) { | ||
297 | return compileDeferred((AggregatorConstraint) constraint, plan, parentPlan, parentCompiled); | ||
298 | } else if (constraint instanceof ExpressionEvaluation) { | ||
299 | return compileDeferred((ExpressionEvaluation) constraint, plan, parentPlan, parentCompiled); | ||
300 | } else if (constraint instanceof TypeFilterConstraint) { | ||
301 | return compileDeferred((TypeFilterConstraint) constraint, plan, parentPlan, parentCompiled); | ||
302 | } | ||
303 | throw new UnsupportedOperationException("Unknown deferred constraint " + constraint); | ||
304 | } | ||
305 | |||
306 | private CompiledSubPlan compileDeferred(Equality constraint, SubPlan plan, SubPlan parentPlan, | ||
307 | CompiledSubPlan parentCompiled) { | ||
308 | if (constraint.isMoot()) | ||
309 | return parentCompiled.cloneFor(plan); | ||
310 | |||
311 | Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); | ||
312 | Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); | ||
313 | |||
314 | if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { | ||
315 | Integer indexLower = Math.min(index1, index2); | ||
316 | Integer indexHigher = Math.max(index1, index2); | ||
317 | |||
318 | EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe(); | ||
319 | equalityFilterRecipe.setParent(parentCompiled.getRecipe()); | ||
320 | equalityFilterRecipe.getIndices().add(indexLower); | ||
321 | equalityFilterRecipe.getIndices().add(indexHigher); | ||
322 | |||
323 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), equalityFilterRecipe, parentCompiled); | ||
324 | } else { | ||
325 | throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", | ||
326 | plan.toShortString(), parentCompiled.toString())); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | /** | ||
331 | * Precondition: constantTrace must map to a ConstantRecipe, and all of its variables must be contained in | ||
332 | * toFilterTrace. | ||
333 | */ | ||
334 | private CompiledSubPlan compileConstantFiltering(SubPlan plan, PlanningTrace toFilterTrace, | ||
335 | ConstantRecipe constantRecipe, List<PVariable> filteredVariables) { | ||
336 | PlanningTrace resultTrace = toFilterTrace; | ||
337 | |||
338 | int constantVariablesSize = filteredVariables.size(); | ||
339 | for (int i = 0; i < constantVariablesSize; ++i) { | ||
340 | Object constantValue = constantRecipe.getConstantValues().get(i); | ||
341 | PVariable filteredVariable = filteredVariables.get(i); | ||
342 | int filteredColumn = resultTrace.getVariablesTuple().indexOf(filteredVariable); | ||
343 | |||
344 | DiscriminatorDispatcherRecipe dispatcherRecipe = FACTORY.createDiscriminatorDispatcherRecipe(); | ||
345 | dispatcherRecipe.setDiscriminationColumnIndex(filteredColumn); | ||
346 | dispatcherRecipe.setParent(resultTrace.getRecipe()); | ||
347 | |||
348 | PlanningTrace dispatcherTrace = new PlanningTrace(plan, resultTrace.getVariablesTuple(), dispatcherRecipe, | ||
349 | resultTrace); | ||
350 | |||
351 | DiscriminatorBucketRecipe bucketRecipe = FACTORY.createDiscriminatorBucketRecipe(); | ||
352 | bucketRecipe.setBucketKey(constantValue); | ||
353 | bucketRecipe.setParent(dispatcherRecipe); | ||
354 | |||
355 | PlanningTrace bucketTrace = new PlanningTrace(plan, dispatcherTrace.getVariablesTuple(), bucketRecipe, | ||
356 | dispatcherTrace); | ||
357 | |||
358 | resultTrace = bucketTrace; | ||
359 | } | ||
360 | |||
361 | return resultTrace.cloneFor(plan); | ||
362 | } | ||
363 | |||
364 | private CompiledSubPlan compileDeferred(ExportedParameter constraint, SubPlan plan, SubPlan parentPlan, | ||
365 | CompiledSubPlan parentCompiled) { | ||
366 | return parentCompiled.cloneFor(plan); | ||
367 | } | ||
368 | |||
369 | private CompiledSubPlan compileDeferred(Inequality constraint, SubPlan plan, SubPlan parentPlan, | ||
370 | CompiledSubPlan parentCompiled) { | ||
371 | if (constraint.isEliminable()) | ||
372 | return parentCompiled.cloneFor(plan); | ||
373 | |||
374 | Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); | ||
375 | Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); | ||
376 | |||
377 | if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { | ||
378 | Integer indexLower = Math.min(index1, index2); | ||
379 | Integer indexHigher = Math.max(index1, index2); | ||
380 | |||
381 | InequalityFilterRecipe inequalityFilterRecipe = FACTORY.createInequalityFilterRecipe(); | ||
382 | inequalityFilterRecipe.setParent(parentCompiled.getRecipe()); | ||
383 | inequalityFilterRecipe.setSubject(indexLower); | ||
384 | inequalityFilterRecipe.getInequals().add(indexHigher); | ||
385 | |||
386 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), inequalityFilterRecipe, | ||
387 | parentCompiled); | ||
388 | } else { | ||
389 | throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", | ||
390 | plan.toShortString(), parentCompiled.toString())); | ||
391 | } | ||
392 | } | ||
393 | |||
394 | private CompiledSubPlan compileDeferred(TypeFilterConstraint constraint, SubPlan plan, SubPlan parentPlan, | ||
395 | CompiledSubPlan parentCompiled) { | ||
396 | final IInputKey inputKey = constraint.getInputKey(); | ||
397 | if (!metaContext.isStateless(inputKey)) | ||
398 | throw new UnsupportedOperationException( | ||
399 | "Non-enumerable input keys are currently supported in Rete only if they are stateless, unlike " | ||
400 | + inputKey); | ||
401 | |||
402 | final Tuple constraintVariables = constraint.getVariablesTuple(); | ||
403 | final List<PVariable> parentVariables = parentCompiled.getVariablesTuple(); | ||
404 | |||
405 | Mask mask; // select elements of the tuple to check against extensional relation | ||
406 | if (Tuples.flatTupleOf(parentVariables.toArray()).equals(constraintVariables)) { | ||
407 | mask = null; // lucky case, parent signature equals that of input key | ||
408 | } else { | ||
409 | List<PVariable> variables = new ArrayList<PVariable>(); | ||
410 | for (Object variable : constraintVariables.getElements()) { | ||
411 | variables.add((PVariable) variable); | ||
412 | } | ||
413 | mask = CompilerHelper.makeProjectionMask(parentCompiled, variables); | ||
414 | } | ||
415 | InputFilterRecipe inputFilterRecipe = RecipesHelper.inputFilterRecipe(parentCompiled.getRecipe(), inputKey, | ||
416 | inputKey.getStringID(), mask); | ||
417 | return new CompiledSubPlan(plan, parentVariables, inputFilterRecipe, parentCompiled); | ||
418 | } | ||
419 | |||
420 | private CompiledSubPlan compileDeferred(NegativePatternCall constraint, SubPlan plan, SubPlan parentPlan, | ||
421 | CompiledSubPlan parentCompiled) { | ||
422 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, | ||
423 | constraint.getActualParametersTuple()); | ||
424 | |||
425 | JoinHelper joinHelper = new JoinHelper(plan, parentCompiled, callTrace); | ||
426 | final RecipeTraceInfo primaryIndexer = joinHelper.getPrimaryIndexer(); | ||
427 | final RecipeTraceInfo secondaryIndexer = joinHelper.getSecondaryIndexer(); | ||
428 | |||
429 | AntiJoinRecipe antiJoinRecipe = FACTORY.createAntiJoinRecipe(); | ||
430 | antiJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | ||
431 | antiJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe()); | ||
432 | |||
433 | return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), antiJoinRecipe, primaryIndexer, | ||
434 | secondaryIndexer); | ||
435 | } | ||
436 | |||
437 | private CompiledSubPlan compileDeferred(PatternMatchCounter constraint, SubPlan plan, SubPlan parentPlan, | ||
438 | CompiledSubPlan parentCompiled) { | ||
439 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, | ||
440 | constraint.getActualParametersTuple()); | ||
441 | |||
442 | // hack: use some mask computations (+ the indexers) from a fake natural join against the called query | ||
443 | JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace); | ||
444 | final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); | ||
445 | final RecipeTraceInfo callProjectionIndexer = fakeJoinHelper.getSecondaryIndexer(); | ||
446 | |||
447 | final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>( | ||
448 | fakeJoinHelper.getSecondaryMask().transform(callTrace.getVariablesTuple())); | ||
449 | /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable()); | ||
450 | |||
451 | CountAggregatorRecipe aggregatorRecipe = FACTORY.createCountAggregatorRecipe(); | ||
452 | aggregatorRecipe.setParent((ProjectionIndexerRecipe) callProjectionIndexer.getRecipe()); | ||
453 | PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, | ||
454 | callProjectionIndexer); | ||
455 | |||
456 | IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); | ||
457 | aggregatorIndexerRecipe.setParent(aggregatorRecipe); | ||
458 | // aggregatorIndexerRecipe.setMask(RecipesHelper.mask( | ||
459 | // sideVariablesTuple.size(), | ||
460 | // //use same indices as in the projection indexer | ||
461 | // // EVEN if result variable already visible in left parent | ||
462 | // fakeJoinHelper.getSecondaryMask().indices | ||
463 | // )); | ||
464 | |||
465 | int aggregatorWidth = sideVariablesTuple.size(); | ||
466 | int aggregateResultIndex = aggregatorWidth - 1; | ||
467 | |||
468 | aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( | ||
469 | // aggregate according all but the last index | ||
470 | aggregateResultIndex, aggregatorWidth))); | ||
471 | PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, | ||
472 | aggregatorTrace); | ||
473 | |||
474 | JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); | ||
475 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | ||
476 | naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); | ||
477 | naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, | ||
478 | // extend with last element only - the computation value | ||
479 | aggregateResultIndex)); | ||
480 | |||
481 | // what if the new variable already has a value? | ||
482 | // even if already known, we add the new result variable, so that it can be filtered at the end | ||
483 | // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); | ||
484 | |||
485 | final List<PVariable> aggregatedVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); | ||
486 | aggregatedVariablesTuple.add(constraint.getResultVariable()); | ||
487 | |||
488 | PlanningTrace joinTrace = new PlanningTrace(plan, aggregatedVariablesTuple, naturalJoinRecipe, primaryIndexer, | ||
489 | aggregatorIndexerTrace); | ||
490 | |||
491 | return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); | ||
492 | // if (!alreadyKnown) { | ||
493 | // return joinTrace.cloneFor(plan); | ||
494 | // } else { | ||
495 | // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); | ||
496 | // } | ||
497 | } | ||
498 | |||
499 | private CompiledSubPlan compileDeferred(AggregatorConstraint constraint, SubPlan plan, SubPlan parentPlan, | ||
500 | CompiledSubPlan parentCompiled) { | ||
501 | final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, | ||
502 | constraint.getActualParametersTuple()); | ||
503 | |||
504 | // hack: use some mask computations (+ the indexers) from a fake natural join against the called query | ||
505 | JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace); | ||
506 | final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); | ||
507 | TupleMask callGroupMask = fakeJoinHelper.getSecondaryMask(); | ||
508 | |||
509 | final List<PVariable> sideVariablesTuple = new ArrayList<PVariable>( | ||
510 | callGroupMask.transform(callTrace.getVariablesTuple())); | ||
511 | /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable()); | ||
512 | |||
513 | IMultisetAggregationOperator<?, ?, ?> operator = constraint.getAggregator().getOperator(); | ||
514 | |||
515 | SingleColumnAggregatorRecipe columnAggregatorRecipe = FACTORY.createSingleColumnAggregatorRecipe(); | ||
516 | columnAggregatorRecipe.setParent(callTrace.getRecipe()); | ||
517 | columnAggregatorRecipe.setMultisetAggregationOperator(operator); | ||
518 | |||
519 | int columnIndex = constraint.getAggregatedColumn(); | ||
520 | IPosetComparator posetComparator = null; | ||
521 | Mask groupMask = CompilerHelper.toRecipeMask(callGroupMask); | ||
522 | |||
523 | // temporary solution to support the deprecated option for now | ||
524 | final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan)); | ||
525 | |||
526 | columnAggregatorRecipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); | ||
527 | if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { | ||
528 | List<PParameter> parameters = constraint.getReferredQuery().getParameters(); | ||
529 | IInputKey key = parameters.get(columnIndex).getDeclaredUnaryType(); | ||
530 | if (key != null && metaContext.isPosetKey(key)) { | ||
531 | posetComparator = metaContext.getPosetComparator(Collections.singleton(key)); | ||
532 | } | ||
533 | } | ||
534 | |||
535 | if (posetComparator == null) { | ||
536 | columnAggregatorRecipe.setGroupByMask(groupMask); | ||
537 | columnAggregatorRecipe.setAggregableIndex(columnIndex); | ||
538 | } else { | ||
539 | MonotonicityInfo monotonicityInfo = FACTORY.createMonotonicityInfo(); | ||
540 | monotonicityInfo.setCoreMask(groupMask); | ||
541 | monotonicityInfo.setPosetMask(CompilerHelper.toRecipeMask( | ||
542 | TupleMask.selectSingle(columnIndex, constraint.getActualParametersTuple().getSize()))); | ||
543 | monotonicityInfo.setPosetComparator(posetComparator); | ||
544 | columnAggregatorRecipe.setOptionalMonotonicityInfo(monotonicityInfo); | ||
545 | } | ||
546 | |||
547 | ReteNodeRecipe aggregatorRecipe = columnAggregatorRecipe; | ||
548 | PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, callTrace); | ||
549 | |||
550 | IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); | ||
551 | aggregatorIndexerRecipe.setParent(aggregatorRecipe); | ||
552 | |||
553 | int aggregatorWidth = sideVariablesTuple.size(); | ||
554 | int aggregateResultIndex = aggregatorWidth - 1; | ||
555 | |||
556 | aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( | ||
557 | // aggregate according all but the last index | ||
558 | aggregateResultIndex, aggregatorWidth))); | ||
559 | PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, | ||
560 | aggregatorTrace); | ||
561 | |||
562 | JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); | ||
563 | naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); | ||
564 | naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); | ||
565 | naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, | ||
566 | // extend with last element only - the computation value | ||
567 | aggregateResultIndex)); | ||
568 | |||
569 | // what if the new variable already has a value? | ||
570 | // even if already known, we add the new result variable, so that it can be filtered at the end | ||
571 | // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); | ||
572 | |||
573 | final List<PVariable> finalVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); | ||
574 | finalVariablesTuple.add(constraint.getResultVariable()); | ||
575 | |||
576 | PlanningTrace joinTrace = new PlanningTrace(plan, finalVariablesTuple, naturalJoinRecipe, primaryIndexer, | ||
577 | aggregatorIndexerTrace); | ||
578 | |||
579 | return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); | ||
580 | // if (!alreadyKnown) { | ||
581 | // return joinTrace.cloneFor(plan); | ||
582 | // } else { | ||
583 | // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); | ||
584 | // } | ||
585 | } | ||
586 | |||
587 | private CompiledSubPlan compileDeferred(ExpressionEvaluation constraint, SubPlan plan, SubPlan parentPlan, | ||
588 | CompiledSubPlan parentCompiled) { | ||
589 | Map<String, Integer> tupleNameMap = new HashMap<String, Integer>(); | ||
590 | for (String name : constraint.getEvaluator().getInputParameterNames()) { | ||
591 | Map<? extends Object, Integer> index = parentCompiled.getPosMapping(); | ||
592 | PVariable variable = constraint.getPSystem().getVariableByNameChecked(name); | ||
593 | Integer position = index.get(variable); | ||
594 | tupleNameMap.put(name, position); | ||
595 | } | ||
596 | |||
597 | final PVariable outputVariable = constraint.getOutputVariable(); | ||
598 | final boolean booleanCheck = outputVariable == null; | ||
599 | |||
600 | // TODO determine whether expression is costly | ||
601 | boolean cacheOutput = ReteHintOptions.cacheOutputOfEvaluatorsByDefault.getValueOrDefault(getHints(plan)); | ||
602 | // for (PAnnotation pAnnotation : | ||
603 | // plan.getBody().getPattern().getAnnotationsByName(EXPRESSION_EVALUATION_ANNOTATION"")) { | ||
604 | // for (Object value : pAnnotation.getAllValues("expensive")) { | ||
605 | // if (value instanceof Boolean) | ||
606 | // cacheOutput = (boolean) value; | ||
607 | // } | ||
608 | // } | ||
609 | |||
610 | ExpressionEnforcerRecipe enforcerRecipe = booleanCheck ? FACTORY.createCheckRecipe() | ||
611 | : FACTORY.createEvalRecipe(); | ||
612 | enforcerRecipe.setParent(parentCompiled.getRecipe()); | ||
613 | enforcerRecipe.setExpression(RecipesHelper.expressionDefinition(constraint.getEvaluator())); | ||
614 | enforcerRecipe.setCacheOutput(cacheOutput); | ||
615 | if (enforcerRecipe instanceof EvalRecipe) { | ||
616 | ((EvalRecipe) enforcerRecipe).setUnwinding(constraint.isUnwinding()); | ||
617 | } | ||
618 | for (Entry<String, Integer> entry : tupleNameMap.entrySet()) { | ||
619 | enforcerRecipe.getMappedIndices().put(entry.getKey(), entry.getValue()); | ||
620 | } | ||
621 | |||
622 | final List<PVariable> enforcerVariablesTuple = new ArrayList<PVariable>(parentCompiled.getVariablesTuple()); | ||
623 | if (!booleanCheck) | ||
624 | enforcerVariablesTuple.add(outputVariable); | ||
625 | PlanningTrace enforcerTrace = new PlanningTrace(plan, enforcerVariablesTuple, enforcerRecipe, parentCompiled); | ||
626 | |||
627 | return CompilerHelper.checkAndTrimEqualVariables(plan, enforcerTrace).cloneFor(plan); | ||
628 | } | ||
629 | |||
630 | private CompiledSubPlan doCompileJoin(PJoin operation, SubPlan plan) { | ||
631 | final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan); | ||
632 | final CompiledSubPlan leftCompiled = compiledParents.get(0); | ||
633 | final CompiledSubPlan rightCompiled = compiledParents.get(1); | ||
634 | |||
635 | return compileToNaturalJoin(plan, leftCompiled, rightCompiled); | ||
636 | } | ||
637 | |||
638 | private CompiledSubPlan compileToNaturalJoin(SubPlan plan, final PlanningTrace leftCompiled, | ||
639 | final PlanningTrace rightCompiled) { | ||
640 | // CHECK IF SPECIAL CASE | ||
641 | |||
642 | // Is constant filtering applicable? | ||
643 | if (ReteHintOptions.useDiscriminatorDispatchersForConstantFiltering.getValueOrDefault(getHints(plan))) { | ||
644 | if (leftCompiled.getRecipe() instanceof ConstantRecipe | ||
645 | && rightCompiled.getVariablesTuple().containsAll(leftCompiled.getVariablesTuple())) { | ||
646 | return compileConstantFiltering(plan, rightCompiled, (ConstantRecipe) leftCompiled.getRecipe(), | ||
647 | leftCompiled.getVariablesTuple()); | ||
648 | } | ||
649 | if (rightCompiled.getRecipe() instanceof ConstantRecipe | ||
650 | && leftCompiled.getVariablesTuple().containsAll(rightCompiled.getVariablesTuple())) { | ||
651 | return compileConstantFiltering(plan, leftCompiled, (ConstantRecipe) rightCompiled.getRecipe(), | ||
652 | rightCompiled.getVariablesTuple()); | ||
653 | } | ||
654 | } | ||
655 | |||
656 | // ELSE: ACTUAL JOIN | ||
657 | JoinHelper joinHelper = new JoinHelper(plan, leftCompiled, rightCompiled); | ||
658 | return new CompiledSubPlan(plan, joinHelper.getNaturalJoinVariablesTuple(), joinHelper.getNaturalJoinRecipe(), | ||
659 | joinHelper.getPrimaryIndexer(), joinHelper.getSecondaryIndexer()); | ||
660 | } | ||
661 | |||
662 | private CompiledSubPlan doCompileProject(PProject operation, SubPlan plan) { | ||
663 | final List<CompiledSubPlan> compiledParents = getCompiledFormOfParents(plan); | ||
664 | final CompiledSubPlan compiledParent = compiledParents.get(0); | ||
665 | |||
666 | List<PVariable> projectedVariables = new ArrayList<PVariable>(operation.getToVariables()); | ||
667 | // Determinizing projection: try to keep original order (hopefully facilitates node reuse) | ||
668 | Map<PVariable, Integer> parentPosMapping = compiledParent.getPosMapping(); | ||
669 | Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get)); | ||
670 | |||
671 | return doProjectPlan(compiledParent, projectedVariables, true, | ||
672 | parentTrace -> parentTrace.cloneFor(plan), | ||
673 | (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace), | ||
674 | (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace) | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | /** | ||
679 | * Projects a subplan onto the specified variable tuple | ||
680 | * @param compiledParentPlan the compiled form of the subplan | ||
681 | * @param targetVariables list of variables to project to | ||
682 | * @param enforceUniqueness whether distinctness shall be enforced after the projection. | ||
683 | * Specify false only if directly connecting to a production node. | ||
684 | * @param reinterpretTraceFactory constructs a reinterpreted trace that simply relabels the compiled parent plan, in case it is sufficient | ||
685 | * @param intermediateTraceFactory constructs a recipe trace for an intermediate node, given the recipe of the node and its parent trace | ||
686 | * @param finalTraceFactory constructs a recipe trace for the final resulting node, given the recipe of the node and its parent trace | ||
687 | * @since 2.1 | ||
688 | */ | ||
689 | <ResultTrace extends RecipeTraceInfo> ResultTrace doProjectPlan( | ||
690 | final CompiledSubPlan compiledParentPlan, | ||
691 | final List<PVariable> targetVariables, | ||
692 | boolean enforceUniqueness, | ||
693 | Function<CompiledSubPlan, ResultTrace> reinterpretTraceFactory, | ||
694 | BiFunction<ReteNodeRecipe, RecipeTraceInfo, RecipeTraceInfo> intermediateTraceFactory, | ||
695 | BiFunction<ReteNodeRecipe, RecipeTraceInfo, ResultTrace> finalTraceFactory) | ||
696 | { | ||
697 | if (targetVariables.equals(compiledParentPlan.getVariablesTuple())) // no projection needed | ||
698 | return reinterpretTraceFactory.apply(compiledParentPlan); | ||
699 | |||
700 | // otherwise, we need at least a trimmer | ||
701 | TrimmerRecipe trimmerRecipe = CompilerHelper.makeTrimmerRecipe(compiledParentPlan, targetVariables); | ||
702 | |||
703 | // do we need to eliminate duplicates? | ||
704 | SubPlan parentPlan = compiledParentPlan.getSubPlan(); | ||
705 | if (!enforceUniqueness || BuildHelper.areAllVariablesDetermined( | ||
706 | parentPlan, | ||
707 | targetVariables, | ||
708 | queryAnalyzer, | ||
709 | true)) | ||
710 | { | ||
711 | // if uniqueness enforcess is unwanted or unneeeded, skip it | ||
712 | return finalTraceFactory.apply(trimmerRecipe, compiledParentPlan); | ||
713 | } else { | ||
714 | // add a uniqueness enforcer | ||
715 | UniquenessEnforcerRecipe recipe = FACTORY.createUniquenessEnforcerRecipe(); | ||
716 | recipe.getParents().add(trimmerRecipe); | ||
717 | |||
718 | // temporary solution to support the deprecated option for now | ||
719 | final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan)); | ||
720 | |||
721 | recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); | ||
722 | if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { | ||
723 | PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, parentPlan.getBody(), metaContext); | ||
724 | |||
725 | if (triplet.comparator != null) { | ||
726 | MonotonicityInfo info = FACTORY.createMonotonicityInfo(); | ||
727 | info.setCoreMask(triplet.coreMask); | ||
728 | info.setPosetMask(triplet.posetMask); | ||
729 | info.setPosetComparator(triplet.comparator); | ||
730 | recipe.setOptionalMonotonicityInfo(info); | ||
731 | } | ||
732 | } | ||
733 | |||
734 | RecipeTraceInfo trimmerTrace = intermediateTraceFactory.apply(trimmerRecipe, compiledParentPlan); | ||
735 | return finalTraceFactory.apply(recipe, trimmerTrace); | ||
736 | } | ||
737 | } | ||
738 | |||
739 | /** | ||
740 | * Projects the final compiled form of a PBody onto the parameter tuple | ||
741 | * @param compiledBody the compiled form of the body, with all constraints enforced, not yet projected to query parameters | ||
742 | * @param enforceUniqueness whether distinctness shall be enforced after the projection. | ||
743 | * Specify false only if directly connecting to a production node. | ||
744 | * @since 2.1 | ||
745 | */ | ||
746 | RecipeTraceInfo projectBodyFinalToParameters( | ||
747 | final CompiledSubPlan compiledBody, | ||
748 | boolean enforceUniqueness) | ||
749 | { | ||
750 | final PBody body = compiledBody.getSubPlan().getBody(); | ||
751 | final List<PVariable> parameterList = body.getSymbolicParameterVariables(); | ||
752 | |||
753 | return doProjectPlan(compiledBody, parameterList, enforceUniqueness, | ||
754 | parentTrace -> parentTrace, | ||
755 | (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace), | ||
756 | (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace) | ||
757 | ); | ||
758 | } | ||
759 | |||
760 | private CompiledSubPlan doCompileStart(PStart operation, SubPlan plan) { | ||
761 | if (!operation.getAPrioriVariables().isEmpty()) { | ||
762 | throw new IllegalArgumentException("Input variables unsupported by Rete: " + plan.toShortString()); | ||
763 | } | ||
764 | final ConstantRecipe recipe = FACTORY.createConstantRecipe(); | ||
765 | recipe.getConstantValues().clear(); | ||
766 | |||
767 | return new CompiledSubPlan(plan, new ArrayList<PVariable>(), recipe); | ||
768 | } | ||
769 | |||
770 | private CompiledSubPlan doCompileEnumerate(EnumerablePConstraint constraint, SubPlan plan) { | ||
771 | final PlanningTrace trimmedTrace = doEnumerateAndDeduplicate(constraint, plan); | ||
772 | |||
773 | return trimmedTrace.cloneFor(plan); | ||
774 | } | ||
775 | |||
776 | private PlanningTrace doEnumerateAndDeduplicate(EnumerablePConstraint constraint, SubPlan plan) { | ||
777 | final PlanningTrace coreTrace = doEnumerateDispatch(plan, constraint); | ||
778 | final PlanningTrace trimmedTrace = CompilerHelper.checkAndTrimEqualVariables(plan, coreTrace); | ||
779 | return trimmedTrace; | ||
780 | } | ||
781 | |||
782 | private PlanningTrace doEnumerateDispatch(SubPlan plan, EnumerablePConstraint constraint) { | ||
783 | if (constraint instanceof RelationEvaluation) { | ||
784 | return compileEnumerable(plan, (RelationEvaluation) constraint); | ||
785 | } else if (constraint instanceof BinaryTransitiveClosure) { | ||
786 | return compileEnumerable(plan, (BinaryTransitiveClosure) constraint); | ||
787 | } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { | ||
788 | return compileEnumerable(plan, (BinaryReflexiveTransitiveClosure) constraint); | ||
789 | } else if (constraint instanceof RepresentativeElectionConstraint) { | ||
790 | return compileEnumerable(plan, (RepresentativeElectionConstraint) constraint); | ||
791 | } else if (constraint instanceof ConstantValue) { | ||
792 | return compileEnumerable(plan, (ConstantValue) constraint); | ||
793 | } else if (constraint instanceof PositivePatternCall) { | ||
794 | return compileEnumerable(plan, (PositivePatternCall) constraint); | ||
795 | } else if (constraint instanceof TypeConstraint) { | ||
796 | return compileEnumerable(plan, (TypeConstraint) constraint); | ||
797 | } | ||
798 | throw new UnsupportedOperationException("Unknown enumerable constraint " + constraint); | ||
799 | } | ||
800 | |||
801 | private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveClosure constraint) { | ||
802 | // TODO the implementation would perform better if an inequality check would be used after tcRecipe and | ||
803 | // uniqueness enforcer be replaced by a transparent node with multiple parents, but such a node is not available | ||
804 | // in recipe metamodel in VIATRA 2.0 | ||
805 | |||
806 | // Find called query | ||
807 | final PQuery referredQuery = constraint.getSupplierKey(); | ||
808 | final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); | ||
809 | |||
810 | // Calculate irreflexive transitive closure | ||
811 | final TransitiveClosureRecipe tcRecipe = FACTORY.createTransitiveClosureRecipe(); | ||
812 | tcRecipe.setParent(callTrace.getRecipe()); | ||
813 | final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), tcRecipe, callTrace); | ||
814 | |||
815 | // Enumerate universe type | ||
816 | final IInputKey inputKey = constraint.getUniverseType(); | ||
817 | final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); | ||
818 | final PlanningTrace universeTypeTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple( | ||
819 | Tuples.staticArityFlatTupleOf(constraint.getVariablesTuple().get(0))), universeTypeRecipe); | ||
820 | |||
821 | // Calculate reflexive access by duplicating universe type column | ||
822 | final TrimmerRecipe reflexiveRecipe = FACTORY.createTrimmerRecipe(); | ||
823 | reflexiveRecipe.setMask(RecipesHelper.mask(1, 0, 0)); | ||
824 | reflexiveRecipe.setParent(universeTypeRecipe); | ||
825 | final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), reflexiveRecipe, universeTypeTrace); | ||
826 | |||
827 | // Finally, reduce duplicates after a join | ||
828 | final UniquenessEnforcerRecipe brtcRecipe = FACTORY.createUniquenessEnforcerRecipe(); | ||
829 | brtcRecipe.getParents().add(tcRecipe); | ||
830 | brtcRecipe.getParents().add(reflexiveRecipe); | ||
831 | |||
832 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, tcTrace); | ||
833 | } | ||
834 | |||
835 | private PlanningTrace compileEnumerable(SubPlan plan, RepresentativeElectionConstraint constraint) { | ||
836 | var referredQuery = constraint.getSupplierKey(); | ||
837 | var callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); | ||
838 | var recipe = FACTORY.createRepresentativeElectionRecipe(); | ||
839 | recipe.setParent(callTrace.getRecipe()); | ||
840 | recipe.setConnectivity(constraint.getConnectivity()); | ||
841 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); | ||
842 | } | ||
843 | |||
844 | private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure constraint) { | ||
845 | final PQuery referredQuery = constraint.getSupplierKey(); | ||
846 | final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); | ||
847 | |||
848 | final TransitiveClosureRecipe recipe = FACTORY.createTransitiveClosureRecipe(); | ||
849 | recipe.setParent(callTrace.getRecipe()); | ||
850 | |||
851 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); | ||
852 | } | ||
853 | |||
854 | private PlanningTrace compileEnumerable(SubPlan plan, RelationEvaluation constraint) { | ||
855 | final List<ReteNodeRecipe> parentRecipes = new ArrayList<ReteNodeRecipe>(); | ||
856 | final List<RecipeTraceInfo> parentTraceInfos = new ArrayList<RecipeTraceInfo>(); | ||
857 | for (final PQuery inputQuery : constraint.getReferredQueries()) { | ||
858 | final CompiledQuery compiledQuery = getCompiledForm(inputQuery); | ||
859 | parentRecipes.add(compiledQuery.getRecipe()); | ||
860 | parentTraceInfos.add(compiledQuery); | ||
861 | } | ||
862 | final RelationEvaluationRecipe recipe = FACTORY.createRelationEvaluationRecipe(); | ||
863 | recipe.getParents().addAll(parentRecipes); | ||
864 | recipe.setEvaluator(RecipesHelper.expressionDefinition(constraint.getEvaluator())); | ||
865 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, parentTraceInfos); | ||
866 | } | ||
867 | |||
868 | private PlanningTrace compileEnumerable(SubPlan plan, PositivePatternCall constraint) { | ||
869 | final PQuery referredQuery = constraint.getReferredQuery(); | ||
870 | return referQuery(referredQuery, plan, constraint.getVariablesTuple()); | ||
871 | } | ||
872 | |||
873 | private PlanningTrace compileEnumerable(SubPlan plan, TypeConstraint constraint) { | ||
874 | final IInputKey inputKey = constraint.getSupplierKey(); | ||
875 | final InputRecipe recipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); | ||
876 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); | ||
877 | } | ||
878 | |||
879 | private PlanningTrace compileEnumerable(SubPlan plan, ConstantValue constraint) { | ||
880 | final ConstantRecipe recipe = FACTORY.createConstantRecipe(); | ||
881 | recipe.getConstantValues().add(constraint.getSupplierKey()); | ||
882 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); | ||
883 | } | ||
884 | |||
885 | // TODO handle recursion | ||
886 | private PlanningTrace referQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) { | ||
887 | RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query); | ||
888 | return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(actualParametersTuple), | ||
889 | referredQueryTrace.getRecipe(), referredQueryTrace.getParentRecipeTracesForCloning()); | ||
890 | } | ||
891 | |||
892 | private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) { | ||
893 | // eliminate superfluous production node? | ||
894 | if (PVisibility.EMBEDDED == query.getVisibility()) { // currently inline patterns only | ||
895 | Set<PBody> rewrittenBodies = normalizer.rewrite(query).getBodies(); | ||
896 | if (1 == rewrittenBodies.size()) { // non-disjunctive | ||
897 | // TODO in the future, check if non-recursive - (not currently permitted) | ||
898 | |||
899 | PBody pBody = rewrittenBodies.iterator().next(); | ||
900 | SubPlan bodyFinalPlan = getPlan(pBody); | ||
901 | |||
902 | // skip over any projections at the end | ||
903 | bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); | ||
904 | |||
905 | // TODO checkAndTrimEqualVariables may introduce superfluous trim, | ||
906 | // but whatever (no uniqueness enforcer needed) | ||
907 | |||
908 | // compile body | ||
909 | final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); | ||
910 | |||
911 | // project to parameter list, add uniqueness enforcer if necessary | ||
912 | return projectBodyFinalToParameters(compiledBody, true /* ensure uniqueness, as no production node is used */); | ||
913 | } | ||
914 | } | ||
915 | |||
916 | // otherwise, regular reference to recipe realizing the query | ||
917 | return getCompiledForm(query); | ||
918 | } | ||
919 | |||
920 | protected List<CompiledSubPlan> getCompiledFormOfParents(SubPlan plan) { | ||
921 | List<CompiledSubPlan> results = new ArrayList<CompiledSubPlan>(); | ||
922 | for (SubPlan parentPlan : plan.getParentPlans()) { | ||
923 | results.add(getCompiledForm(parentPlan)); | ||
924 | } | ||
925 | return results; | ||
926 | } | ||
927 | |||
928 | /** | ||
929 | * Returns an unmodifiable view of currently cached compiled queries. | ||
930 | */ | ||
931 | public Map<PQuery, CompiledQuery> getCachedCompiledQueries() { | ||
932 | return Collections.unmodifiableMap(queryCompilerCache); | ||
933 | } | ||
934 | |||
935 | /** | ||
936 | * Returns an unmodifiable view of currently cached query plans. | ||
937 | */ | ||
938 | public Map<PBody, SubPlan> getCachedQueryPlans() { | ||
939 | return Collections.unmodifiableMap(plannerCache); | ||
940 | } | ||
941 | |||
942 | private QueryEvaluationHint getHints(SubPlan plan) { | ||
943 | return getHints(plan.getBody().getPattern()); | ||
944 | } | ||
945 | |||
946 | private QueryEvaluationHint getHints(PQuery pattern) { | ||
947 | return hintProvider.getQueryEvaluationHint(pattern); | ||
948 | } | ||
949 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java new file mode 100644 index 00000000..45350099 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java | |||
@@ -0,0 +1,162 @@ | |||
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.rete.construction.quasitree; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collections; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | import java.util.stream.Stream; | ||
19 | |||
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.helpers.FunctionalDependencyHelper; | ||
23 | import tools.refinery.viatra.runtime.matchers.planning.operations.PJoin; | ||
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.util.CollectionsFactory; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * | ||
32 | */ | ||
33 | class JoinCandidate { | ||
34 | private QueryAnalyzer analyzer; | ||
35 | |||
36 | SubPlan primary; | ||
37 | SubPlan secondary; | ||
38 | |||
39 | Set<PVariable> varPrimary; | ||
40 | Set<PVariable> varSecondary; | ||
41 | Set<PVariable> varCommon; | ||
42 | |||
43 | List<PConstraint> consPrimary; | ||
44 | List<PConstraint> consSecondary; | ||
45 | |||
46 | |||
47 | JoinCandidate(SubPlan primary, SubPlan secondary, QueryAnalyzer analyzer) { | ||
48 | super(); | ||
49 | this.primary = primary; | ||
50 | this.secondary = secondary; | ||
51 | this.analyzer = analyzer; | ||
52 | |||
53 | varPrimary = getPrimary().getVisibleVariables(); | ||
54 | varSecondary = getSecondary().getVisibleVariables(); | ||
55 | varCommon = CollectionsFactory.createSet(varPrimary); | ||
56 | varCommon.retainAll(varSecondary); | ||
57 | |||
58 | consPrimary = new ArrayList<PConstraint>(primary.getAllEnforcedConstraints()); | ||
59 | Collections.sort(consPrimary, TieBreaker.CONSTRAINT_COMPARATOR); | ||
60 | consSecondary = new ArrayList<PConstraint>(secondary.getAllEnforcedConstraints()); | ||
61 | Collections.sort(consSecondary, TieBreaker.CONSTRAINT_COMPARATOR); | ||
62 | } | ||
63 | |||
64 | |||
65 | |||
66 | /** | ||
67 | * @return the a | ||
68 | */ | ||
69 | public SubPlan getPrimary() { | ||
70 | return primary; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * @return the b | ||
75 | */ | ||
76 | public SubPlan getSecondary() { | ||
77 | return secondary; | ||
78 | } | ||
79 | |||
80 | public SubPlan getJoinedPlan(SubPlanFactory factory) { | ||
81 | // check special cases first | ||
82 | if (isTrivial()) | ||
83 | return primary; | ||
84 | if (isSubsumption()) | ||
85 | return | ||
86 | (consPrimary.size() > consSecondary.size()) ? primary : secondary; | ||
87 | |||
88 | |||
89 | // default case | ||
90 | return factory.createSubPlan(new PJoin(), primary, secondary); | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | public String toString() { | ||
95 | return primary.toString() + " |x| " + secondary.toString(); | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * @return the varPrimary | ||
100 | */ | ||
101 | public Set<PVariable> getVarPrimary() { | ||
102 | return varPrimary; | ||
103 | } | ||
104 | |||
105 | /** | ||
106 | * @return the varSecondary | ||
107 | */ | ||
108 | public Set<PVariable> getVarSecondary() { | ||
109 | return varSecondary; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @return constraints of primary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}. | ||
114 | */ | ||
115 | public List<PConstraint> getConsPrimary() { | ||
116 | return consPrimary; | ||
117 | } | ||
118 | /** | ||
119 | * @return constraints of secondary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}. | ||
120 | */ | ||
121 | public List<PConstraint> getConsSecondary() { | ||
122 | return consSecondary; | ||
123 | } | ||
124 | |||
125 | |||
126 | |||
127 | public boolean isTrivial() { | ||
128 | return getPrimary().equals(getSecondary()); | ||
129 | } | ||
130 | |||
131 | public boolean isSubsumption() { | ||
132 | return consPrimary.containsAll(consSecondary) || consSecondary.containsAll(consPrimary); | ||
133 | } | ||
134 | |||
135 | public boolean isCheckOnly() { | ||
136 | return varPrimary.containsAll(varSecondary) || varSecondary.containsAll(varPrimary); | ||
137 | } | ||
138 | |||
139 | public boolean isDescartes() { | ||
140 | return Collections.disjoint(varPrimary, varSecondary); | ||
141 | } | ||
142 | |||
143 | private Boolean heath; | ||
144 | |||
145 | // it is a Heath-join iff common variables functionally determine either all primary or all secondary variables | ||
146 | public boolean isHeath() { | ||
147 | if (heath == null) { | ||
148 | Set<PConstraint> union = Stream.concat( | ||
149 | primary.getAllEnforcedConstraints().stream(), | ||
150 | secondary.getAllEnforcedConstraints().stream() | ||
151 | ).collect(Collectors.toSet()); | ||
152 | Map<Set<PVariable>, Set<PVariable>> dependencies = | ||
153 | analyzer.getFunctionalDependencies(union, false); | ||
154 | // does varCommon determine either varPrimary or varSecondary? | ||
155 | Set<PVariable> varCommonClosure = FunctionalDependencyHelper.closureOf(varCommon, dependencies); | ||
156 | |||
157 | heath = varCommonClosure.containsAll(varPrimary) || varCommonClosure.containsAll(varSecondary); | ||
158 | } | ||
159 | return heath; | ||
160 | } | ||
161 | |||
162 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java new file mode 100644 index 00000000..0ea7c1d9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java | |||
@@ -0,0 +1,49 @@ | |||
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.rete.construction.quasitree; | ||
11 | |||
12 | import java.util.Comparator; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
15 | import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent; | ||
16 | |||
17 | /** | ||
18 | * @author Gabor Bergmann | ||
19 | * | ||
20 | */ | ||
21 | public class JoinOrderingHeuristics implements Comparator<JoinCandidate> { | ||
22 | |||
23 | @Override | ||
24 | public int compare(JoinCandidate jc1, JoinCandidate jc2) { | ||
25 | return new OrderingCompareAgent<JoinCandidate>(jc1, jc2) { | ||
26 | @Override | ||
27 | protected void doCompare() { | ||
28 | swallowBoolean(true && consider(preferTrue(a.isTrivial(), b.isTrivial())) | ||
29 | && consider(preferTrue(a.isSubsumption(), b.isSubsumption())) | ||
30 | && consider(preferTrue(a.isCheckOnly(), b.isCheckOnly())) | ||
31 | && consider( | ||
32 | Options.functionalDependencyOption == Options.FunctionalDependencyOption.OFF ? | ||
33 | dontCare() : | ||
34 | preferTrue(a.isHeath(), b.isHeath()) | ||
35 | ) | ||
36 | && consider(preferFalse(a.isDescartes(), b.isDescartes())) | ||
37 | |||
38 | // TODO main heuristic decisions | ||
39 | |||
40 | // tie breaking | ||
41 | && consider(preferLess(a.getConsPrimary(), b.getConsPrimary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR)) | ||
42 | && consider(preferLess(a.getConsSecondary(), b.getConsSecondary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR)) | ||
43 | && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b)))); | ||
44 | } | ||
45 | }.compare(); | ||
46 | |||
47 | } | ||
48 | |||
49 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java new file mode 100644 index 00000000..9b814376 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java | |||
@@ -0,0 +1,205 @@ | |||
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.rete.construction.quasitree; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collections; | ||
14 | import java.util.LinkedHashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import org.apache.log4j.Logger; | ||
19 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
20 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
21 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
22 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
23 | import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; | ||
24 | import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy; | ||
25 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
26 | import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; | ||
27 | import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper; | ||
28 | import tools.refinery.viatra.runtime.matchers.planning.operations.PApply; | ||
29 | import tools.refinery.viatra.runtime.matchers.planning.operations.PEnumerate; | ||
30 | import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; | ||
31 | import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; | ||
32 | import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; | ||
33 | import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; | ||
34 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
35 | import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; | ||
36 | import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; | ||
37 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
38 | import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException; | ||
39 | import tools.refinery.viatra.runtime.rete.util.ReteHintOptions; | ||
40 | |||
41 | /** | ||
42 | * Layout ideas: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=398763 | ||
43 | * | ||
44 | * @author Gabor Bergmann | ||
45 | * | ||
46 | */ | ||
47 | public class QuasiTreeLayout implements IQueryPlannerStrategy { | ||
48 | |||
49 | private IQueryBackendHintProvider hintProvider; | ||
50 | private IQueryBackendContext backendContext; | ||
51 | private QueryAnalyzer queryAnalyzer; | ||
52 | |||
53 | public QuasiTreeLayout(IQueryBackendContext backendContext) { | ||
54 | this(backendContext, backendContext.getHintProvider()); | ||
55 | } | ||
56 | |||
57 | public QuasiTreeLayout(IQueryBackendContext backendContext, IQueryBackendHintProvider hintProvider) { | ||
58 | this.backendContext = backendContext; | ||
59 | this.hintProvider = hintProvider; | ||
60 | queryAnalyzer = backendContext.getQueryAnalyzer(); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context) { | ||
65 | return new Scaffold(pSystem, logger, context).run(); | ||
66 | } | ||
67 | |||
68 | public class Scaffold { | ||
69 | PBody pSystem; | ||
70 | PQuery query; | ||
71 | IQueryMetaContext context; | ||
72 | private QueryEvaluationHint hints; | ||
73 | //IOperationCompiler compiler; | ||
74 | //SubPlanProcessor planProcessor = new SubPlanProcessor(); | ||
75 | SubPlanFactory planFactory; | ||
76 | |||
77 | Set<DeferredPConstraint> deferredConstraints = null; | ||
78 | Set<EnumerablePConstraint> enumerableConstraints = null; | ||
79 | Set<ConstantValue> constantConstraints = null; | ||
80 | Set<SubPlan> forefront = new LinkedHashSet<SubPlan>(); | ||
81 | Logger logger; | ||
82 | |||
83 | Scaffold(PBody pSystem, Logger logger, /*IOperationCompiler compiler,*/ IQueryMetaContext context) { | ||
84 | this.pSystem = pSystem; | ||
85 | this.logger = logger; | ||
86 | this.context = context; | ||
87 | this.planFactory = new SubPlanFactory(pSystem); | ||
88 | query = pSystem.getPattern(); | ||
89 | //this.compiler = compiler; | ||
90 | //planProcessor.setCompiler(compiler); | ||
91 | |||
92 | hints = hintProvider.getQueryEvaluationHint(query); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * @throws ViatraQueryRuntimeException | ||
97 | */ | ||
98 | public SubPlan run() { | ||
99 | try { | ||
100 | logger.debug(String.format( | ||
101 | "%s: patternbody build started for %s", | ||
102 | getClass().getSimpleName(), | ||
103 | query.getFullyQualifiedName())); | ||
104 | |||
105 | // PROCESS CONSTRAINTS | ||
106 | deferredConstraints = pSystem.getConstraintsOfType(DeferredPConstraint.class); | ||
107 | enumerableConstraints = pSystem.getConstraintsOfType(EnumerablePConstraint.class); | ||
108 | constantConstraints = pSystem.getConstraintsOfType(ConstantValue.class); | ||
109 | |||
110 | for (EnumerablePConstraint enumerable : enumerableConstraints) { | ||
111 | SubPlan plan = planFactory.createSubPlan(new PEnumerate(enumerable)); | ||
112 | admitSubPlan(plan); | ||
113 | } | ||
114 | if (enumerableConstraints.isEmpty()) { // EXTREME CASE | ||
115 | SubPlan plan = planFactory.createSubPlan(new PStart()); | ||
116 | admitSubPlan(plan); | ||
117 | } | ||
118 | |||
119 | // JOIN FOREFRONT PLANS WHILE POSSIBLE | ||
120 | while (forefront.size() > 1) { | ||
121 | // TODO QUASI-TREE TRIVIAL JOINS? | ||
122 | |||
123 | List<JoinCandidate> candidates = generateJoinCandidates(); | ||
124 | JoinOrderingHeuristics ordering = new JoinOrderingHeuristics(); | ||
125 | JoinCandidate selectedJoin = Collections.min(candidates, ordering); | ||
126 | doJoin(selectedJoin); | ||
127 | } | ||
128 | assert (forefront.size() == 1); | ||
129 | |||
130 | // PROJECT TO PARAMETERS | ||
131 | SubPlan preFinalPlan = forefront.iterator().next(); | ||
132 | SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), preFinalPlan); | ||
133 | |||
134 | // FINAL CHECK, whether all exported variables are present + all constraint satisfied | ||
135 | BuildHelper.finalCheck(pSystem, finalPlan, context); | ||
136 | // TODO integrate the check above in SubPlan / POperation | ||
137 | |||
138 | logger.debug(String.format( | ||
139 | "%s: patternbody query plan concluded for %s as: %s", | ||
140 | getClass().getSimpleName(), | ||
141 | query.getFullyQualifiedName(), | ||
142 | finalPlan.toLongString())); | ||
143 | return finalPlan; | ||
144 | } catch (RetePatternBuildException ex) { | ||
145 | ex.setPatternDescription(query); | ||
146 | throw ex; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | public List<JoinCandidate> generateJoinCandidates() { | ||
151 | List<JoinCandidate> candidates = new ArrayList<JoinCandidate>(); | ||
152 | int bIndex = 0; | ||
153 | for (SubPlan b : forefront) { | ||
154 | int aIndex = 0; | ||
155 | for (SubPlan a : forefront) { | ||
156 | if (aIndex++ >= bIndex) | ||
157 | break; | ||
158 | candidates.add(new JoinCandidate(a, b, queryAnalyzer)); | ||
159 | } | ||
160 | bIndex++; | ||
161 | } | ||
162 | return candidates; | ||
163 | } | ||
164 | |||
165 | private void admitSubPlan(SubPlan plan) { | ||
166 | // are there any unapplied constant filters that we can apply here? | ||
167 | if (ReteHintOptions.prioritizeConstantFiltering.getValueOrDefault(hints)) { | ||
168 | for (ConstantValue constantConstraint : constantConstraints) { | ||
169 | if (!plan.getAllEnforcedConstraints().contains(constantConstraint) && | ||
170 | plan.getVisibleVariables().containsAll(constantConstraint.getAffectedVariables())) { | ||
171 | plan = planFactory.createSubPlan(new PApply(constantConstraint), plan); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | // are there any variables that will not be needed anymore and are worth trimming? | ||
176 | // (check only if there are unenforced enumerables, so that there are still upcoming joins) | ||
177 | // if (Options.planTrimOption != Options.PlanTrimOption.OFF && | ||
178 | // !plan.getAllEnforcedConstraints().containsAll(enumerableConstraints)) { | ||
179 | if (true) { | ||
180 | final SubPlan trimmed = BuildHelper.trimUnneccessaryVariables( | ||
181 | planFactory, plan, true, queryAnalyzer); | ||
182 | plan = trimmed; | ||
183 | } | ||
184 | // are there any checkable constraints? | ||
185 | for (DeferredPConstraint deferred : deferredConstraints) { | ||
186 | if (!plan.getAllEnforcedConstraints().contains(deferred)) { | ||
187 | if (deferred.isReadyAt(plan, context)) { | ||
188 | admitSubPlan(planFactory.createSubPlan(new PApply(deferred), plan)); | ||
189 | return; | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | // if no checkable constraints and no unused variables | ||
194 | forefront.add(plan); | ||
195 | } | ||
196 | |||
197 | private void doJoin(JoinCandidate selectedJoin) { | ||
198 | forefront.remove(selectedJoin.getPrimary()); | ||
199 | forefront.remove(selectedJoin.getSecondary()); | ||
200 | admitSubPlan(selectedJoin.getJoinedPlan(planFactory)); | ||
201 | } | ||
202 | |||
203 | } | ||
204 | |||
205 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java new file mode 100644 index 00000000..0b955922 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java | |||
@@ -0,0 +1,30 @@ | |||
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.rete.construction.quasitree; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; | ||
14 | import tools.refinery.viatra.runtime.rete.util.LexicographicComparator; | ||
15 | |||
16 | /** | ||
17 | * Class providing comparators for breaking ties somewhat more deterministically. | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public class TieBreaker { | ||
22 | |||
23 | private TieBreaker() {/*Utility class constructor*/} | ||
24 | |||
25 | public static final Comparator<PConstraint> CONSTRAINT_COMPARATOR = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID(); | ||
26 | |||
27 | public static final Comparator<Iterable<? extends PConstraint>> CONSTRAINT_LIST_COMPARATOR = | ||
28 | new LexicographicComparator<PConstraint>(CONSTRAINT_COMPARATOR); | ||
29 | |||
30 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java new file mode 100644 index 00000000..d32a0449 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java | |||
@@ -0,0 +1,65 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.rete.eval; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
15 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
16 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
17 | import tools.refinery.viatra.runtime.rete.single.SingleInputNode; | ||
18 | |||
19 | /** | ||
20 | * @author Bergmann Gabor | ||
21 | */ | ||
22 | public abstract class AbstractEvaluatorNode extends SingleInputNode implements IEvaluatorNode { | ||
23 | |||
24 | /** | ||
25 | * @since 1.5 | ||
26 | */ | ||
27 | protected EvaluatorCore core; | ||
28 | |||
29 | |||
30 | /** | ||
31 | * @since 1.5 | ||
32 | */ | ||
33 | public AbstractEvaluatorNode(ReteContainer reteContainer, EvaluatorCore core) { | ||
34 | super(reteContainer); | ||
35 | this.core = core; | ||
36 | core.init(this); | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * @since 1.5 | ||
41 | */ | ||
42 | @Override | ||
43 | public ReteContainer getReteContainer() { | ||
44 | return getContainer(); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @since 1.5 | ||
49 | */ | ||
50 | @Override | ||
51 | public String prettyPrintTraceInfoPatternList() { | ||
52 | return getTraceInfoPatternsEnumerated(); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * @since 2.4 | ||
57 | */ | ||
58 | protected void propagateIterableUpdate(final Direction direction, final Iterable<Tuple> update, final Timestamp timestamp) { | ||
59 | final Iterator<Tuple> itr = update.iterator(); | ||
60 | while (itr.hasNext()) { | ||
61 | propagateUpdate(direction, itr.next(), timestamp); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java new file mode 100644 index 00000000..c45c6048 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java | |||
@@ -0,0 +1,180 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.rete.eval; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import org.apache.log4j.Logger; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
19 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.TupleValueProvider; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Sets; | ||
23 | |||
24 | /** | ||
25 | * An instance of this class performs the evaluation of Java expressions. | ||
26 | * | ||
27 | * @author Bergmann Gabor | ||
28 | * @author Tamas Szabo | ||
29 | * @since 1.5 | ||
30 | */ | ||
31 | public abstract class EvaluatorCore { | ||
32 | |||
33 | protected Logger logger; | ||
34 | protected IExpressionEvaluator evaluator; | ||
35 | /** | ||
36 | * @since 2.4 | ||
37 | */ | ||
38 | protected int sourceTupleWidth; | ||
39 | private Map<String, Integer> parameterPositions; | ||
40 | protected IQueryRuntimeContext runtimeContext; | ||
41 | protected IEvaluatorNode evaluatorNode; | ||
42 | |||
43 | public EvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, | ||
44 | final Map<String, Integer> parameterPositions, final int sourceTupleWidth) { | ||
45 | this.logger = logger; | ||
46 | this.evaluator = evaluator; | ||
47 | this.parameterPositions = parameterPositions; | ||
48 | this.sourceTupleWidth = sourceTupleWidth; | ||
49 | } | ||
50 | |||
51 | public void init(final IEvaluatorNode evaluatorNode) { | ||
52 | this.evaluatorNode = evaluatorNode; | ||
53 | this.runtimeContext = evaluatorNode.getReteContainer().getNetwork().getEngine().getRuntimeContext(); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * @since 2.4 | ||
58 | */ | ||
59 | public abstract Iterable<Tuple> performEvaluation(final Tuple input); | ||
60 | |||
61 | protected abstract String evaluationKind(); | ||
62 | |||
63 | public Object evaluateTerm(final Tuple input) { | ||
64 | // actual evaluation | ||
65 | Object result = null; | ||
66 | try { | ||
67 | final TupleValueProvider tupleParameters = new TupleValueProvider(runtimeContext.unwrapTuple(input), | ||
68 | parameterPositions); | ||
69 | result = evaluator.evaluateExpression(tupleParameters); | ||
70 | } catch (final Exception e) { | ||
71 | logger.warn(String.format( | ||
72 | "The incremental pattern matcher encountered an error during %s evaluation for pattern(s) %s over values %s. Error message: %s. (Developer note: %s in %s)", | ||
73 | evaluationKind(), evaluatorNode.prettyPrintTraceInfoPatternList(), prettyPrintTuple(input), | ||
74 | e.getMessage(), e.getClass().getSimpleName(), this.evaluatorNode), e); | ||
75 | result = errorResult(); | ||
76 | } | ||
77 | |||
78 | return result; | ||
79 | } | ||
80 | |||
81 | protected String prettyPrintTuple(final Tuple tuple) { | ||
82 | return tuple.toString(); | ||
83 | } | ||
84 | |||
85 | protected Object errorResult() { | ||
86 | return null; | ||
87 | } | ||
88 | |||
89 | public static class PredicateEvaluatorCore extends EvaluatorCore { | ||
90 | |||
91 | public PredicateEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, | ||
92 | final Map<String, Integer> parameterPositions, final int sourceTupleWidth) { | ||
93 | super(logger, evaluator, parameterPositions, sourceTupleWidth); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Iterable<Tuple> performEvaluation(final Tuple input) { | ||
98 | final Object result = evaluateTerm(input); | ||
99 | if (Boolean.TRUE.equals(result)) { | ||
100 | return Collections.singleton(input); | ||
101 | } else { | ||
102 | return null; | ||
103 | } | ||
104 | } | ||
105 | |||
106 | @Override | ||
107 | protected String evaluationKind() { | ||
108 | return "check()"; | ||
109 | } | ||
110 | |||
111 | } | ||
112 | |||
113 | public static class FunctionEvaluatorCore extends EvaluatorCore { | ||
114 | |||
115 | /** | ||
116 | * @since 2.4 | ||
117 | */ | ||
118 | protected final boolean isUnwinding; | ||
119 | |||
120 | public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, | ||
121 | final Map<String, Integer> parameterPositions, final int sourceTupleWidth) { | ||
122 | this(logger, evaluator, parameterPositions, sourceTupleWidth, false); | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * @since 2.4 | ||
127 | */ | ||
128 | public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, | ||
129 | final Map<String, Integer> parameterPositions, final int sourceTupleWidth, final boolean isUnwinding) { | ||
130 | super(logger, evaluator, parameterPositions, sourceTupleWidth); | ||
131 | this.isUnwinding = isUnwinding; | ||
132 | } | ||
133 | |||
134 | @Override | ||
135 | public Iterable<Tuple> performEvaluation(final Tuple input) { | ||
136 | final Object result = evaluateTerm(input); | ||
137 | if (result != null) { | ||
138 | if (this.isUnwinding) { | ||
139 | final Set<?> resultAsSet = (result instanceof Set<?>) ? (Set<?>) result | ||
140 | : (result instanceof Iterable<?>) ? Sets.newSet((Iterable<?>) result) : null; | ||
141 | |||
142 | if (resultAsSet != null) { | ||
143 | return () -> { | ||
144 | final Iterator<?> wrapped = resultAsSet.iterator(); | ||
145 | return new Iterator<Tuple>() { | ||
146 | @Override | ||
147 | public boolean hasNext() { | ||
148 | return wrapped.hasNext(); | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public Tuple next() { | ||
153 | final Object next = wrapped.next(); | ||
154 | return Tuples.staticArityLeftInheritanceTupleOf(input, | ||
155 | runtimeContext.wrapElement(next)); | ||
156 | } | ||
157 | }; | ||
158 | }; | ||
159 | } else { | ||
160 | throw new IllegalStateException( | ||
161 | "This is an unwinding evaluator, which expects the evaluation result to either be a set or an iterable, but it was " | ||
162 | + result); | ||
163 | } | ||
164 | } else { | ||
165 | return Collections.singleton( | ||
166 | Tuples.staticArityLeftInheritanceTupleOf(input, runtimeContext.wrapElement(result))); | ||
167 | } | ||
168 | } else { | ||
169 | return null; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | @Override | ||
174 | protected String evaluationKind() { | ||
175 | return "eval" + (this.isUnwinding ? "Unwind" : "") + "()"; | ||
176 | } | ||
177 | |||
178 | } | ||
179 | |||
180 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java new file mode 100644 index 00000000..177433ab --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java | |||
@@ -0,0 +1,25 @@ | |||
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.rete.eval; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
12 | |||
13 | /** | ||
14 | * This interface is required for the communication between the evaluation core end the evaluator node. | ||
15 | * @author Gabor Bergmann | ||
16 | * @since 1.5 | ||
17 | */ | ||
18 | public interface IEvaluatorNode { | ||
19 | |||
20 | ReteContainer getReteContainer(); | ||
21 | |||
22 | String prettyPrintTraceInfoPatternList(); | ||
23 | |||
24 | |||
25 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java new file mode 100644 index 00000000..8928645c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java | |||
@@ -0,0 +1,75 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.rete.eval; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Map; | ||
15 | import java.util.Map.Entry; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
23 | |||
24 | /** | ||
25 | * @author Bergmann Gabor | ||
26 | * | ||
27 | */ | ||
28 | public class MemorylessEvaluatorNode extends AbstractEvaluatorNode { | ||
29 | |||
30 | /** | ||
31 | * @since 1.5 | ||
32 | */ | ||
33 | public MemorylessEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) { | ||
34 | super(reteContainer, core); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
39 | final Collection<Tuple> parentTuples = new ArrayList<Tuple>(); | ||
40 | propagatePullInto(parentTuples, flush); | ||
41 | for (final Tuple parentTuple : parentTuples) { | ||
42 | final Iterable<Tuple> output = core.performEvaluation(parentTuple); | ||
43 | if (output != null) { | ||
44 | final Iterator<Tuple> itr = output.iterator(); | ||
45 | while (itr.hasNext()) { | ||
46 | collector.add(itr.next()); | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
54 | final Map<Tuple, Timeline<Timestamp>> parentTuples = CollectionsFactory.createMap(); | ||
55 | propagatePullIntoWithTimestamp(parentTuples, flush); | ||
56 | for (final Entry<Tuple, Timeline<Timestamp>> entry : parentTuples.entrySet()) { | ||
57 | final Iterable<Tuple> output = core.performEvaluation(entry.getKey()); | ||
58 | if (output != null) { | ||
59 | final Iterator<Tuple> itr = output.iterator(); | ||
60 | while (itr.hasNext()) { | ||
61 | collector.put(itr.next(), entry.getValue()); | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { | ||
69 | final Iterable<Tuple> output = core.performEvaluation(input); | ||
70 | if (output != null) { | ||
71 | propagateIterableUpdate(direction, output, timestamp); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java new file mode 100644 index 00000000..40a20c4e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java | |||
@@ -0,0 +1,311 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, 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.rete.eval; | ||
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.Map.Entry; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
26 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; | ||
27 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
28 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
29 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; | ||
31 | |||
32 | /** | ||
33 | * An evaluator node that caches the evaluation result. This node is also capable of caching the timestamps associated | ||
34 | * with the result tuples if it is used in recursive differential dataflow evaluation. | ||
35 | * | ||
36 | * @author Bergmann Gabor | ||
37 | * @author Tamas Szabo | ||
38 | */ | ||
39 | public class OutputCachingEvaluatorNode extends AbstractEvaluatorNode implements Clearable, ResumableNode { | ||
40 | |||
41 | /** | ||
42 | * @since 2.3 | ||
43 | */ | ||
44 | protected NetworkStructureChangeSensitiveLogic logic; | ||
45 | |||
46 | /** | ||
47 | * @since 2.4 | ||
48 | */ | ||
49 | protected Map<Tuple, Iterable<Tuple>> outputCache; | ||
50 | |||
51 | /** | ||
52 | * Maps input tuples to timestamps. It is wrong to map evaluation result to timestamps because the different input | ||
53 | * tuples may yield the same evaluation result. This field is null as long as this node is in a non-recursive group. | ||
54 | * | ||
55 | * @since 2.4 | ||
56 | */ | ||
57 | protected TimelyMemory<Timestamp> memory; | ||
58 | |||
59 | /** | ||
60 | * @since 2.4 | ||
61 | */ | ||
62 | protected CommunicationGroup group; | ||
63 | |||
64 | /** | ||
65 | * @since 1.5 | ||
66 | */ | ||
67 | public OutputCachingEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) { | ||
68 | super(reteContainer, core); | ||
69 | reteContainer.registerClearable(this); | ||
70 | this.outputCache = CollectionsFactory.createMap(); | ||
71 | this.logic = createLogic(); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public CommunicationGroup getCurrentGroup() { | ||
76 | return this.group; | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public void setCurrentGroup(final CommunicationGroup group) { | ||
81 | this.group = group; | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public void networkStructureChanged() { | ||
86 | super.networkStructureChanged(); | ||
87 | this.logic = createLogic(); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public void clear() { | ||
92 | this.outputCache.clear(); | ||
93 | if (this.memory != null) { | ||
94 | this.memory.clear(); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * @since 2.3 | ||
100 | */ | ||
101 | protected NetworkStructureChangeSensitiveLogic createLogic() { | ||
102 | if (this.reteContainer.isTimelyEvaluation() | ||
103 | && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
104 | if (this.memory == null) { | ||
105 | this.memory = new TimelyMemory<Timestamp>(reteContainer.isTimelyEvaluation() && reteContainer | ||
106 | .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); | ||
107 | } | ||
108 | return TIMELY; | ||
109 | } else { | ||
110 | return TIMELESS; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
116 | this.logic.pullInto(collector, flush); | ||
117 | } | ||
118 | |||
119 | @Override | ||
120 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
121 | this.logic.pullIntoWithTimeline(collector, flush); | ||
122 | } | ||
123 | |||
124 | @Override | ||
125 | public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { | ||
126 | this.logic.update(direction, input, timestamp); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * @since 2.4 | ||
131 | */ | ||
132 | @Override | ||
133 | public Timestamp getResumableTimestamp() { | ||
134 | if (this.memory == null) { | ||
135 | return null; | ||
136 | } else { | ||
137 | return this.memory.getResumableTimestamp(); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * @since 2.4 | ||
143 | */ | ||
144 | @Override | ||
145 | public void resumeAt(final Timestamp timestamp) { | ||
146 | this.logic.resumeAt(timestamp); | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * @since 2.3 | ||
151 | */ | ||
152 | protected static abstract class NetworkStructureChangeSensitiveLogic { | ||
153 | |||
154 | /** | ||
155 | * @since 2.4 | ||
156 | */ | ||
157 | public abstract void update(final Direction direction, final Tuple input, final Timestamp timestamp); | ||
158 | |||
159 | public abstract void pullInto(final Collection<Tuple> collector, final boolean flush); | ||
160 | |||
161 | /** | ||
162 | * @since 2.4 | ||
163 | */ | ||
164 | public abstract void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush); | ||
165 | |||
166 | /** | ||
167 | * @since 2.4 | ||
168 | */ | ||
169 | public abstract void resumeAt(final Timestamp timestamp); | ||
170 | |||
171 | } | ||
172 | |||
173 | private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { | ||
174 | |||
175 | @Override | ||
176 | public void resumeAt(final Timestamp timestamp) { | ||
177 | // there is nothing to resume in the timeless case because we do not even care about timestamps | ||
178 | } | ||
179 | |||
180 | @Override | ||
181 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
182 | throw new UnsupportedOperationException(); | ||
183 | } | ||
184 | |||
185 | @Override | ||
186 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
187 | for (final Iterable<Tuple> output : outputCache.values()) { | ||
188 | if (output != NORESULT) { | ||
189 | final Iterator<Tuple> itr = output.iterator(); | ||
190 | while (itr.hasNext()) { | ||
191 | collector.add(itr.next()); | ||
192 | } | ||
193 | } | ||
194 | } | ||
195 | } | ||
196 | |||
197 | @Override | ||
198 | public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { | ||
199 | if (direction == Direction.INSERT) { | ||
200 | final Iterable<Tuple> output = core.performEvaluation(input); | ||
201 | if (output != null) { | ||
202 | final Iterable<Tuple> previous = outputCache.put(input, output); | ||
203 | if (previous != null) { | ||
204 | throw new IllegalStateException( | ||
205 | String.format("Duplicate insertion of tuple %s into node %s", input, this)); | ||
206 | } | ||
207 | propagateIterableUpdate(direction, output, timestamp); | ||
208 | } | ||
209 | } else { | ||
210 | final Iterable<Tuple> output = outputCache.remove(input); | ||
211 | if (output != null) { | ||
212 | // may be null if no result was yielded | ||
213 | propagateIterableUpdate(direction, output, timestamp); | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | }; | ||
218 | |||
219 | private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { | ||
220 | |||
221 | @Override | ||
222 | public void resumeAt(final Timestamp timestamp) { | ||
223 | final Map<Tuple, Diff<Timestamp>> diffMap = memory.resumeAt(timestamp); | ||
224 | |||
225 | for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) { | ||
226 | final Tuple input = entry.getKey(); | ||
227 | final Iterable<Tuple> output = outputCache.get(input); | ||
228 | if (output != NORESULT) { | ||
229 | for (final Signed<Timestamp> signed : entry.getValue()) { | ||
230 | propagateIterableUpdate(signed.getDirection(), output, signed.getPayload()); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | if (memory.get(input) == null) { | ||
235 | outputCache.remove(input); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | final Timestamp nextTimestamp = memory.getResumableTimestamp(); | ||
240 | if (nextTimestamp != null) { | ||
241 | group.notifyHasMessage(mailbox, nextTimestamp); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | @Override | ||
246 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
247 | for (final Entry<Tuple, Timeline<Timestamp>> entry : memory.asMap().entrySet()) { | ||
248 | final Tuple input = entry.getKey(); | ||
249 | final Iterable<Tuple> output = outputCache.get(input); | ||
250 | if (output != NORESULT) { | ||
251 | final Timeline<Timestamp> timestamp = entry.getValue(); | ||
252 | final Iterator<Tuple> itr = output.iterator(); | ||
253 | while (itr.hasNext()) { | ||
254 | collector.put(itr.next(), timestamp); | ||
255 | } | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | |||
260 | @Override | ||
261 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
262 | TIMELESS.pullInto(collector, flush); | ||
263 | } | ||
264 | |||
265 | @Override | ||
266 | public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { | ||
267 | if (direction == Direction.INSERT) { | ||
268 | Iterable<Tuple> output = outputCache.get(input); | ||
269 | if (output == null) { | ||
270 | output = core.performEvaluation(input); | ||
271 | if (output == null) { | ||
272 | // the evaluation result is really null | ||
273 | output = NORESULT; | ||
274 | } | ||
275 | outputCache.put(input, output); | ||
276 | } | ||
277 | final Diff<Timestamp> diff = memory.put(input, timestamp); | ||
278 | if (output != NORESULT) { | ||
279 | for (final Signed<Timestamp> signed : diff) { | ||
280 | propagateIterableUpdate(signed.getDirection(), output, signed.getPayload()); | ||
281 | } | ||
282 | } | ||
283 | } else { | ||
284 | final Iterable<Tuple> output = outputCache.get(input); | ||
285 | final Diff<Timestamp> diff = memory.remove(input, timestamp); | ||
286 | if (memory.get(input) == null) { | ||
287 | outputCache.remove(input); | ||
288 | } | ||
289 | if (output != NORESULT) { | ||
290 | for (final Signed<Timestamp> signed : diff) { | ||
291 | propagateIterableUpdate(signed.getDirection(), output, signed.getPayload()); | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | }; | ||
297 | |||
298 | /** | ||
299 | * This field is used to represent the "null" evaluation result. This is an optimization used in the timely case | ||
300 | * where the same tuple may be inserted multiple times with different timestamps. This way, we can also cache if | ||
301 | * something evaluated to null (instead of just forgetting about the previously computed result), thus avoiding the | ||
302 | * need to re-run a potentially expensive evaluation. | ||
303 | */ | ||
304 | private static final Iterable<Tuple> NORESULT = Collections | ||
305 | .singleton(Tuples.staticArityFlatTupleOf(NoResult.INSTANCE)); | ||
306 | |||
307 | private enum NoResult { | ||
308 | INSTANCE | ||
309 | } | ||
310 | |||
311 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java new file mode 100644 index 00000000..68d277e8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java | |||
@@ -0,0 +1,183 @@ | |||
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.rete.eval; | ||
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.Map.Entry; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.RelationEvaluation; | ||
20 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; | ||
25 | import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver; | ||
26 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
27 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
28 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
29 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
30 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
31 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
32 | import tools.refinery.viatra.runtime.rete.single.AbstractUniquenessEnforcerNode; | ||
33 | |||
34 | /** | ||
35 | * A node that operates in batch-style (see {@link Receiver#doesProcessUpdatesInBatch()} and evaluates arbitrary Java | ||
36 | * logic represented by an {@link IRelationEvaluator} on the input relations. This is the backing computation node of a | ||
37 | * {@link RelationEvaluation} constraint. | ||
38 | * | ||
39 | * @author Tamas Szabo | ||
40 | * @since 2.8 | ||
41 | */ | ||
42 | public class RelationEvaluatorNode extends StandardNode implements Supplier, Clearable { | ||
43 | |||
44 | private final IRelationEvaluator evaluator; | ||
45 | private Set<Tuple> cachedOutputs; | ||
46 | private Supplier[] inputSuppliers; | ||
47 | private BatchingReceiver[] inputReceivers; | ||
48 | |||
49 | public RelationEvaluatorNode(final ReteContainer container, final IRelationEvaluator evaluator) { | ||
50 | super(container); | ||
51 | this.evaluator = evaluator; | ||
52 | this.reteContainer.registerClearable(this); | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public void clear() { | ||
57 | this.cachedOutputs.clear(); | ||
58 | } | ||
59 | |||
60 | public void connectToParents(final List<Supplier> inputSuppliers) { | ||
61 | this.inputSuppliers = new Supplier[inputSuppliers.size()]; | ||
62 | this.inputReceivers = new BatchingReceiver[inputSuppliers.size()]; | ||
63 | |||
64 | final List<Integer> inputArities = evaluator.getInputArities(); | ||
65 | |||
66 | if (inputArities.size() != inputSuppliers.size()) { | ||
67 | throw new IllegalStateException(evaluator.toString() + " expects " + inputArities.size() | ||
68 | + " inputs, but got " + inputSuppliers.size() + " input(s)!"); | ||
69 | } | ||
70 | |||
71 | for (int i = 0; i < inputSuppliers.size(); i++) { | ||
72 | final int currentExpectedInputArity = inputArities.get(i); | ||
73 | final Supplier inputSupplier = inputSuppliers.get(i); | ||
74 | // it is expected that the supplier is a production node because | ||
75 | // the corresponding constraint itself accepts a list of PQuery | ||
76 | if (!(inputSupplier instanceof ProductionNode)) { | ||
77 | throw new IllegalStateException( | ||
78 | evaluator.toString() + " expects each one of its suppliers to be instances of " | ||
79 | + ProductionNode.class.getSimpleName() + " but got an instance of " | ||
80 | + inputSupplier.getClass().getSimpleName() + "!"); | ||
81 | } | ||
82 | final int currentActualInputArity = ((ProductionNode) inputSupplier).getPosMapping().size(); | ||
83 | if (currentActualInputArity != currentExpectedInputArity) { | ||
84 | throw new IllegalStateException( | ||
85 | evaluator.toString() + " expects input arity " + currentExpectedInputArity + " at position " + i | ||
86 | + " but got " + currentActualInputArity + "!"); | ||
87 | } | ||
88 | final BatchingReceiver inputReceiver = new BatchingReceiver((ProductionNode) inputSupplier, | ||
89 | this.reteContainer); | ||
90 | this.inputSuppliers[i] = inputSupplier; | ||
91 | this.inputReceivers[i] = inputReceiver; | ||
92 | this.reteContainer.connectAndSynchronize(inputSupplier, inputReceiver); | ||
93 | reteContainer.getCommunicationTracker().registerDependency(inputReceiver, this); | ||
94 | } | ||
95 | |||
96 | // initialize the output relation | ||
97 | final List<Set<Tuple>> inputSets = new ArrayList<Set<Tuple>>(); | ||
98 | for (final BatchingReceiver inputReceiver : this.inputReceivers) { | ||
99 | inputSets.add(inputReceiver.getTuples()); | ||
100 | } | ||
101 | this.cachedOutputs = evaluateRelation(inputSets); | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public void networkStructureChanged() { | ||
106 | if (this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
107 | throw new IllegalStateException(this.toString() + " cannot be used in recursive evaluation!"); | ||
108 | } | ||
109 | super.networkStructureChanged(); | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
114 | collector.addAll(this.cachedOutputs); | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
119 | final Timeline<Timestamp> timeline = Timelines.createFrom(Timestamp.ZERO); | ||
120 | for (final Tuple output : this.cachedOutputs) { | ||
121 | collector.put(output, timeline); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | private Set<Tuple> evaluateRelation(final List<Set<Tuple>> inputs) { | ||
126 | try { | ||
127 | return this.evaluator.evaluateRelation(inputs); | ||
128 | } catch (final Exception e) { | ||
129 | throw new IllegalStateException("Exception during the evaluation of " + this.evaluator.toString() + "!", e); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | private void batchUpdateCompleted() { | ||
134 | final List<Set<Tuple>> inputSets = new ArrayList<Set<Tuple>>(); | ||
135 | for (final BatchingReceiver inputReceiver : this.inputReceivers) { | ||
136 | inputSets.add(inputReceiver.getTuples()); | ||
137 | } | ||
138 | final Set<Tuple> newOutputs = evaluateRelation(inputSets); | ||
139 | for (final Tuple tuple : newOutputs) { | ||
140 | if (this.cachedOutputs != null && this.cachedOutputs.remove(tuple)) { | ||
141 | // already known tuple - do nothing | ||
142 | } else { | ||
143 | // newly inserted tuple | ||
144 | propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO); | ||
145 | } | ||
146 | } | ||
147 | if (this.cachedOutputs != null) { | ||
148 | for (final Tuple tuple : this.cachedOutputs) { | ||
149 | // lost tuple | ||
150 | propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO); | ||
151 | } | ||
152 | } | ||
153 | this.cachedOutputs = newOutputs; | ||
154 | } | ||
155 | |||
156 | public class BatchingReceiver extends SimpleReceiver { | ||
157 | private final ProductionNode source; | ||
158 | |||
159 | private BatchingReceiver(final ProductionNode source, final ReteContainer container) { | ||
160 | super(container); | ||
161 | this.source = source; | ||
162 | } | ||
163 | |||
164 | private Set<Tuple> getTuples() { | ||
165 | return ((AbstractUniquenessEnforcerNode) this.source).getTuples(); | ||
166 | } | ||
167 | |||
168 | @Override | ||
169 | public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { | ||
170 | throw new UnsupportedOperationException("This receiver only supports batch-style operation!"); | ||
171 | } | ||
172 | |||
173 | @Override | ||
174 | public void batchUpdate(final Collection<Entry<Tuple, Integer>> updates, final Timestamp timestamp) { | ||
175 | assert Timestamp.ZERO.equals(timestamp); | ||
176 | // there is nothing to do here because the source production node has already updated itself | ||
177 | // the only thing we need to do is to issue the callback | ||
178 | RelationEvaluatorNode.this.batchUpdateCompleted(); | ||
179 | } | ||
180 | |||
181 | } | ||
182 | |||
183 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java new file mode 100644 index 00000000..6306a482 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java | |||
@@ -0,0 +1,28 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, 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.rete.index; | ||
10 | |||
11 | import java.lang.ref.WeakReference; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
14 | |||
15 | public abstract class DefaultIndexerListener implements IndexerListener { | ||
16 | |||
17 | WeakReference<Node> owner; | ||
18 | |||
19 | public DefaultIndexerListener(Node owner) { | ||
20 | this.owner = new WeakReference<Node>(owner); | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public Node getOwner() { | ||
25 | return owner.get(); | ||
26 | } | ||
27 | |||
28 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java new file mode 100644 index 00000000..170ac460 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java | |||
@@ -0,0 +1,348 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
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.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
21 | import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; | ||
22 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
23 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
24 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
26 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp.AllZeroMap; | ||
27 | import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand; | ||
28 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
29 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
30 | |||
31 | /** | ||
32 | * Abstract superclass for nodes with two inputs that are matched against each other. | ||
33 | * | ||
34 | * @author Gabor Bergmann | ||
35 | */ | ||
36 | public abstract class DualInputNode extends StandardNode implements NetworkStructureChangeSensitiveNode { | ||
37 | |||
38 | /** | ||
39 | * @since 2.3 | ||
40 | */ | ||
41 | protected NetworkStructureChangeSensitiveLogic logic; | ||
42 | |||
43 | public IterableIndexer getPrimarySlot() { | ||
44 | return primarySlot; | ||
45 | } | ||
46 | |||
47 | public Indexer getSecondarySlot() { | ||
48 | return secondarySlot; | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * @author Gabor Bergmann | ||
53 | * | ||
54 | */ | ||
55 | public enum Side { | ||
56 | PRIMARY, SECONDARY, BOTH; | ||
57 | |||
58 | public Side opposite() { | ||
59 | switch (this) { | ||
60 | case PRIMARY: | ||
61 | return SECONDARY; | ||
62 | case SECONDARY: | ||
63 | return PRIMARY; | ||
64 | case BOTH: | ||
65 | return BOTH; | ||
66 | default: | ||
67 | return BOTH; | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Holds the primary input slot of this node. | ||
74 | */ | ||
75 | protected IterableIndexer primarySlot; | ||
76 | |||
77 | /** | ||
78 | * Holds the secondary input slot of this node. | ||
79 | */ | ||
80 | protected Indexer secondarySlot; | ||
81 | |||
82 | /** | ||
83 | * Optional complementer mask | ||
84 | */ | ||
85 | protected TupleMask complementerSecondaryMask; | ||
86 | |||
87 | /** | ||
88 | * true if the primary and secondary slots coincide | ||
89 | */ | ||
90 | protected boolean coincidence; | ||
91 | |||
92 | /** | ||
93 | * @param reteContainer | ||
94 | */ | ||
95 | public DualInputNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) { | ||
96 | super(reteContainer); | ||
97 | this.complementerSecondaryMask = complementerSecondaryMask; | ||
98 | this.indexerGroupCache = CollectionsFactory.createMap(); | ||
99 | this.refreshIndexerGroupCache(); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Should be called only once, when node is initialized. | ||
104 | */ | ||
105 | public void connectToIndexers(final IterableIndexer primarySlot, final Indexer secondarySlot) { | ||
106 | this.primarySlot = primarySlot; | ||
107 | this.secondarySlot = secondarySlot; | ||
108 | |||
109 | reteContainer.getCommunicationTracker().registerDependency(primarySlot, this); | ||
110 | reteContainer.getCommunicationTracker().registerDependency(secondarySlot, this); | ||
111 | |||
112 | // attach listeners | ||
113 | // if there is syncing, do this after the flush done for pulling, but before syncing updates | ||
114 | coincidence = primarySlot.equals(secondarySlot); | ||
115 | |||
116 | if (!coincidence) { // regular case | ||
117 | primarySlot.attachListener(new DefaultIndexerListener(this) { | ||
118 | @Override | ||
119 | public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, | ||
120 | final Tuple signature, final boolean change, final Timestamp timestamp) { | ||
121 | DualInputNode.this.logic.notifyUpdate(Side.PRIMARY, direction, updateElement, signature, change, | ||
122 | timestamp); | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | public String toString() { | ||
127 | return "primary@" + DualInputNode.this; | ||
128 | } | ||
129 | }); | ||
130 | secondarySlot.attachListener(new DefaultIndexerListener(this) { | ||
131 | public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, | ||
132 | final Tuple signature, final boolean change, final Timestamp timestamp) { | ||
133 | DualInputNode.this.logic.notifyUpdate(Side.SECONDARY, direction, updateElement, signature, change, | ||
134 | timestamp); | ||
135 | } | ||
136 | |||
137 | @Override | ||
138 | public String toString() { | ||
139 | return "secondary@" + DualInputNode.this; | ||
140 | } | ||
141 | }); | ||
142 | } else { // if the two slots are the same, updates have to be handled carefully | ||
143 | primarySlot.attachListener(new DefaultIndexerListener(this) { | ||
144 | public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, | ||
145 | final Tuple signature, final boolean change, final Timestamp timestamp) { | ||
146 | DualInputNode.this.logic.notifyUpdate(Side.BOTH, direction, updateElement, signature, change, | ||
147 | timestamp); | ||
148 | } | ||
149 | |||
150 | @Override | ||
151 | public String toString() { | ||
152 | return "both@" + DualInputNode.this; | ||
153 | } | ||
154 | }); | ||
155 | } | ||
156 | |||
157 | for (final Receiver receiver : getReceivers()) { | ||
158 | this.reteContainer.getDelayedCommandQueue() | ||
159 | .add(new DelayedConnectCommand(this, receiver, this.reteContainer)); | ||
160 | } | ||
161 | |||
162 | // Given that connectToIndexers registers new dependencies, the networkStructureChanged() method will be called | ||
163 | // by the CommunicationTracker, and the implementation of that method in turn will call refreshIndexerGroupCache() anyway. | ||
164 | this.refreshIndexerGroupCache(); | ||
165 | } | ||
166 | |||
167 | /** | ||
168 | * Helper: retrieves all stored substitutions from the opposite side memory. | ||
169 | * | ||
170 | * @return the collection of opposite substitutions if any, or null if none | ||
171 | */ | ||
172 | protected Collection<Tuple> retrieveOpposites(final Side side, final Tuple signature) { | ||
173 | return getSlot(side.opposite()).get(signature); | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * @since 2.3 | ||
178 | */ | ||
179 | protected NetworkStructureChangeSensitiveLogic createLogic() { | ||
180 | if (this.reteContainer.isTimelyEvaluation() | ||
181 | && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
182 | return createTimelyLogic(); | ||
183 | } else { | ||
184 | return createTimelessLogic(); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Helper: unifies a left and right partial matching. | ||
190 | */ | ||
191 | protected Tuple unify(final Tuple left, final Tuple right) { | ||
192 | return complementerSecondaryMask.combine(left, right, Options.enableInheritance, true); | ||
193 | } | ||
194 | |||
195 | @Override | ||
196 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
197 | this.logic.pullInto(collector, flush); | ||
198 | } | ||
199 | |||
200 | @Override | ||
201 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
202 | this.logic.pullIntoWithTimeline(collector, flush); | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Helper: unifies a substitution from the specified side with another substitution from the other side. | ||
207 | */ | ||
208 | protected Tuple unify(final Side side, final Tuple ps, final Tuple opposite) { | ||
209 | switch (side) { | ||
210 | case PRIMARY: | ||
211 | return unify(ps, opposite); | ||
212 | case SECONDARY: | ||
213 | return unify(opposite, ps); | ||
214 | case BOTH: | ||
215 | return unify(ps, opposite); | ||
216 | default: | ||
217 | return null; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | /** | ||
222 | * Simulates the behavior of the node for calibration purposes only. | ||
223 | */ | ||
224 | public abstract Tuple calibrate(final Tuple primary, final Tuple secondary); | ||
225 | |||
226 | /** | ||
227 | * @param complementerSecondaryMask | ||
228 | * the complementerSecondaryMask to set | ||
229 | */ | ||
230 | public void setComplementerSecondaryMask(final TupleMask complementerSecondaryMask) { | ||
231 | this.complementerSecondaryMask = complementerSecondaryMask; | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * Retrieves the slot corresponding to the specified side. | ||
236 | */ | ||
237 | protected Indexer getSlot(final Side side) { | ||
238 | if (side == Side.SECONDARY) { | ||
239 | return secondarySlot; | ||
240 | } else { | ||
241 | return primarySlot; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | @Override | ||
246 | public void assignTraceInfo(final TraceInfo traceInfo) { | ||
247 | super.assignTraceInfo(traceInfo); | ||
248 | if (traceInfo.propagateToIndexerParent()) { | ||
249 | if (primarySlot != null) { | ||
250 | primarySlot.acceptPropagatedTraceInfo(traceInfo); | ||
251 | } | ||
252 | if (secondarySlot != null) { | ||
253 | secondarySlot.acceptPropagatedTraceInfo(traceInfo); | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | @Override | ||
259 | public void networkStructureChanged() { | ||
260 | super.networkStructureChanged(); | ||
261 | this.logic = createLogic(); | ||
262 | this.refreshIndexerGroupCache(); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * @since 2.3 | ||
267 | */ | ||
268 | protected abstract NetworkStructureChangeSensitiveLogic createTimelyLogic(); | ||
269 | |||
270 | /** | ||
271 | * @since 2.3 | ||
272 | */ | ||
273 | protected abstract NetworkStructureChangeSensitiveLogic createTimelessLogic(); | ||
274 | |||
275 | /** | ||
276 | * This map caches the result of a CommunicationTracker.areInSameGroup(indexer, this) call. It does that for both | ||
277 | * the primary and secondary slots. This way we can avoid the lookup in the getWithTimestamp call for each tuple. | ||
278 | * The cache needs to be maintained when the network structure changes. | ||
279 | * @since 2.3 | ||
280 | */ | ||
281 | protected Map<Indexer, Boolean> indexerGroupCache; | ||
282 | |||
283 | /** | ||
284 | * @since 2.3 | ||
285 | */ | ||
286 | protected void refreshIndexerGroupCache() { | ||
287 | this.indexerGroupCache.clear(); | ||
288 | if (this.primarySlot != null) { | ||
289 | this.indexerGroupCache.put(this.primarySlot, | ||
290 | this.reteContainer.getCommunicationTracker().areInSameGroup(this.primarySlot, this)); | ||
291 | } | ||
292 | if (this.secondarySlot != null) { | ||
293 | this.indexerGroupCache.put(this.secondarySlot, | ||
294 | this.reteContainer.getCommunicationTracker().areInSameGroup(this.secondarySlot, this)); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | /** | ||
299 | * @since 2.4 | ||
300 | */ | ||
301 | protected Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature, final Indexer indexer) { | ||
302 | if (this.indexerGroupCache.get(indexer)) { | ||
303 | // recursive timely case | ||
304 | return indexer.getTimeline(signature); | ||
305 | } else { | ||
306 | // the indexer is in a different group, treat all of its tuples as they would have timestamp 0 | ||
307 | final Collection<Tuple> tuples = indexer.get(signature); | ||
308 | if (tuples == null) { | ||
309 | return null; | ||
310 | } else { | ||
311 | return new AllZeroMap<Tuple>((Set<Tuple>) tuples); | ||
312 | } | ||
313 | } | ||
314 | } | ||
315 | |||
316 | /** | ||
317 | * @since 2.3 | ||
318 | */ | ||
319 | protected static abstract class NetworkStructureChangeSensitiveLogic { | ||
320 | |||
321 | /** | ||
322 | * Abstract handler for update event. | ||
323 | * | ||
324 | * @param side | ||
325 | * The side on which the event occurred. | ||
326 | * @param direction | ||
327 | * The direction of the update. | ||
328 | * @param updateElement | ||
329 | * The partial matching that is inserted. | ||
330 | * @param signature | ||
331 | * Masked signature of updateElement. | ||
332 | * @param change | ||
333 | * Indicates whether this is/was the first/last instance of this signature in this slot. | ||
334 | * @since 2.4 | ||
335 | */ | ||
336 | public abstract void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, | ||
337 | final Tuple signature, final boolean change, final Timestamp timestamp); | ||
338 | |||
339 | public abstract void pullInto(final Collection<Tuple> collector, final boolean flush); | ||
340 | |||
341 | /** | ||
342 | * @since 2.4 | ||
343 | */ | ||
344 | public abstract void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush); | ||
345 | |||
346 | } | ||
347 | |||
348 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java new file mode 100644 index 00000000..275ff638 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java | |||
@@ -0,0 +1,199 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
19 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | |||
22 | /** | ||
23 | * Propagates all substitutions arriving at the PRIMARY slot if and only if (a matching substitution on the SECONDARY is | ||
24 | * present) xor (NEGATIVE). | ||
25 | * | ||
26 | * The negative parameter specifies whether this node checks for existence or non-existence. | ||
27 | * <p> | ||
28 | * It is mandatory in differential dataflow evaluation that the secondary parent is in an upstream dependency component | ||
29 | * (so that every secondary tuple comes with zero timestamp). | ||
30 | * | ||
31 | * @author Gabor Bergmann | ||
32 | */ | ||
33 | public class ExistenceNode extends DualInputNode { | ||
34 | |||
35 | protected boolean negative; | ||
36 | |||
37 | /** | ||
38 | * @param reteContainer | ||
39 | * @param negative | ||
40 | * if false, act as existence checker, otherwise a nonexistence-checker | ||
41 | */ | ||
42 | public ExistenceNode(final ReteContainer reteContainer, final boolean negative) { | ||
43 | super(reteContainer, null); | ||
44 | this.negative = negative; | ||
45 | this.logic = createLogic(); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Tuple calibrate(final Tuple primary, final Tuple secondary) { | ||
50 | return primary; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public void networkStructureChanged() { | ||
55 | if (this.reteContainer.isTimelyEvaluation() && this.secondarySlot != null | ||
56 | && this.reteContainer.getCommunicationTracker().areInSameGroup(this, this.secondarySlot)) { | ||
57 | throw new IllegalStateException("Secondary parent must be in an upstream dependency component!"); | ||
58 | } | ||
59 | super.networkStructureChanged(); | ||
60 | } | ||
61 | |||
62 | private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { | ||
63 | |||
64 | @Override | ||
65 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
66 | throw new UnsupportedOperationException(); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
71 | if (primarySlot == null || secondarySlot == null) { | ||
72 | return; | ||
73 | } | ||
74 | if (flush) { | ||
75 | reteContainer.flushUpdates(); | ||
76 | } | ||
77 | |||
78 | for (final Tuple signature : primarySlot.getSignatures()) { | ||
79 | // primaries can not be null due to the contract of IterableIndex.getSignatures() | ||
80 | final Collection<Tuple> primaries = primarySlot.get(signature); | ||
81 | final Collection<Tuple> opposites = secondarySlot.get(signature); | ||
82 | if ((opposites != null) ^ negative) { | ||
83 | collector.addAll(primaries); | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, | ||
90 | final Tuple signature, final boolean change, final Timestamp timestamp) { | ||
91 | // in the default case, all timestamps must be zero | ||
92 | assert Timestamp.ZERO.equals(timestamp); | ||
93 | |||
94 | switch (side) { | ||
95 | case PRIMARY: | ||
96 | if ((retrieveOpposites(side, signature) != null) ^ negative) { | ||
97 | propagateUpdate(direction, updateElement, timestamp); | ||
98 | } | ||
99 | break; | ||
100 | case SECONDARY: | ||
101 | if (change) { | ||
102 | final Collection<Tuple> opposites = retrieveOpposites(side, signature); | ||
103 | if (opposites != null) { | ||
104 | for (final Tuple opposite : opposites) { | ||
105 | propagateUpdate((negative ? direction.opposite() : direction), opposite, timestamp); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | break; | ||
110 | case BOTH: | ||
111 | // in case the slots coincide, | ||
112 | // negative --> always empty | ||
113 | // !positive --> identity | ||
114 | if (!negative) { | ||
115 | propagateUpdate(direction, updateElement, timestamp); | ||
116 | } | ||
117 | break; | ||
118 | } | ||
119 | } | ||
120 | }; | ||
121 | |||
122 | private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { | ||
123 | |||
124 | @Override | ||
125 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
126 | if (primarySlot == null || secondarySlot == null) { | ||
127 | return; | ||
128 | } | ||
129 | if (flush) { | ||
130 | reteContainer.flushUpdates(); | ||
131 | } | ||
132 | |||
133 | for (final Tuple signature : primarySlot.getSignatures()) { | ||
134 | // primaries can not be null due to the contract of IterableIndex.getSignatures() | ||
135 | final Map<Tuple, Timeline<Timestamp>> primaries = getTimeline(signature, primarySlot); | ||
136 | // see contract: secondary must be in an upstream SCC | ||
137 | final Collection<Tuple> opposites = secondarySlot.get(signature); | ||
138 | if ((opposites != null) ^ negative) { | ||
139 | for (final Tuple primary : primaries.keySet()) { | ||
140 | collector.put(primary, primaries.get(primary)); | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | @Override | ||
147 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
148 | ExistenceNode.this.TIMELESS.pullInto(collector, flush); | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, | ||
153 | final Tuple signature, final boolean change, final Timestamp timestamp) { | ||
154 | switch (side) { | ||
155 | case PRIMARY: { | ||
156 | final Collection<Tuple> opposites = secondarySlot.get(signature); | ||
157 | if ((opposites != null) ^ negative) { | ||
158 | propagateUpdate(direction, updateElement, timestamp); | ||
159 | } | ||
160 | break; | ||
161 | } | ||
162 | case SECONDARY: { | ||
163 | final Map<Tuple, Timeline<Timestamp>> opposites = primarySlot.getTimeline(signature); | ||
164 | if (change) { | ||
165 | if (opposites != null) { | ||
166 | for (final Tuple opposite : opposites.keySet()) { | ||
167 | for (final Signed<Timestamp> oppositeSigned : opposites.get(opposite).asChangeSequence()) { | ||
168 | final Direction product = direction.multiply(oppositeSigned.getDirection()); | ||
169 | propagateUpdate((negative ? product.opposite() : product), opposite, | ||
170 | oppositeSigned.getPayload()); | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | break; | ||
176 | } | ||
177 | case BOTH: | ||
178 | // in case the slots coincide, | ||
179 | // negative --> always empty | ||
180 | // positive --> identity | ||
181 | if (!negative) { | ||
182 | propagateUpdate(direction, updateElement, timestamp); | ||
183 | } | ||
184 | break; | ||
185 | } | ||
186 | } | ||
187 | }; | ||
188 | |||
189 | @Override | ||
190 | protected NetworkStructureChangeSensitiveLogic createTimelessLogic() { | ||
191 | return this.TIMELESS; | ||
192 | } | ||
193 | |||
194 | @Override | ||
195 | protected NetworkStructureChangeSensitiveLogic createTimelyLogic() { | ||
196 | return this.TIMELY; | ||
197 | } | ||
198 | |||
199 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java new file mode 100644 index 00000000..3de10def --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java | |||
@@ -0,0 +1,76 @@ | |||
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.rete.index; | ||
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.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
20 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
23 | |||
24 | /** | ||
25 | * A generic Indexer capable of indexing along any valid TupleMask. Does not keep track of parents, because will not | ||
26 | * ever pull parents. | ||
27 | * | ||
28 | * @author Gabor Bergmann | ||
29 | * | ||
30 | */ | ||
31 | public class GenericProjectionIndexer extends IndexerWithMemory implements ProjectionIndexer { | ||
32 | |||
33 | public GenericProjectionIndexer(ReteContainer reteContainer, TupleMask mask) { | ||
34 | super(reteContainer, mask); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | protected void update(Direction direction, Tuple updateElement, Tuple signature, boolean change, | ||
39 | Timestamp timestamp) { | ||
40 | propagate(direction, updateElement, signature, change, timestamp); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public Collection<Tuple> get(Tuple signature) { | ||
45 | return memory.get(signature); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Map<Tuple, Timeline<Timestamp>> getTimeline(Tuple signature) { | ||
50 | return memory.getWithTimeline(signature); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Iterator<Tuple> iterator() { | ||
55 | return memory.iterator(); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public Iterable<Tuple> getSignatures() { | ||
60 | return memory.getSignatures(); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * @since 2.0 | ||
65 | */ | ||
66 | @Override | ||
67 | public int getBucketCount() { | ||
68 | return memory.getKeysetSize(); | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public Receiver getActiveNode() { | ||
73 | return this; | ||
74 | } | ||
75 | |||
76 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java new file mode 100644 index 00000000..6c158f2c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2012 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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Iterator; | ||
15 | import java.util.List; | ||
16 | |||
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.rete.network.Node; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
24 | |||
25 | /** | ||
26 | * Defines an abstract trivial indexer that identically projects the contents of some stateful node, and can therefore | ||
27 | * save space. Can only exist in connection with a stateful store, and must be operated by another node (the active | ||
28 | * node). Do not attach parents directly! | ||
29 | * | ||
30 | * @author Gabor Bergmann | ||
31 | * @noimplement Rely on the provided implementations | ||
32 | * @noreference Use only via standard Node and Indexer interfaces | ||
33 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
34 | */ | ||
35 | public abstract class IdentityIndexer extends SpecializedProjectionIndexer { | ||
36 | |||
37 | protected abstract Collection<Tuple> getTuples(); | ||
38 | |||
39 | public IdentityIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent, | ||
40 | Node activeNode, List<ListenerSubscription> sharedSubscriptionList) { | ||
41 | super(reteContainer, TupleMask.identity(tupleWidth), parent, activeNode, sharedSubscriptionList); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Collection<Tuple> get(Tuple signature) { | ||
46 | if (contains(signature)) { | ||
47 | return Collections.singleton(signature); | ||
48 | } else | ||
49 | return null; | ||
50 | } | ||
51 | |||
52 | protected boolean contains(Tuple signature) { | ||
53 | return getTuples().contains(signature); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public Collection<Tuple> getSignatures() { | ||
58 | return getTuples(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public int getBucketCount() { | ||
63 | return getTuples().size(); | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public Iterator<Tuple> iterator() { | ||
68 | return getTuples().iterator(); | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
73 | listener.notifyIndexerUpdate(direction, updateElement, updateElement, true, timestamp); | ||
74 | } | ||
75 | |||
76 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java new file mode 100644 index 00000000..fc9d7781 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java | |||
@@ -0,0 +1,71 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | |||
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.timeline.Timeline; | ||
18 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
19 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | |||
22 | /** | ||
23 | * A node that indexes incoming Tuples by their signatures as specified by a TupleMask. Notifies listeners about such | ||
24 | * update events through the IndexerListener. | ||
25 | * | ||
26 | * Signature tuples are created by transforming the update tuples using the mask. Tuples stored with the same signature | ||
27 | * are grouped together. The group or a reduction thereof is retrievable. | ||
28 | * | ||
29 | * @author Gabor Bergmann | ||
30 | */ | ||
31 | public interface Indexer extends Node { | ||
32 | /** | ||
33 | * @return the mask by which the contents are indexed. | ||
34 | */ | ||
35 | public TupleMask getMask(); | ||
36 | |||
37 | /** | ||
38 | * @return the node whose contents are indexed. | ||
39 | */ | ||
40 | public Supplier getParent(); | ||
41 | |||
42 | /** | ||
43 | * @return all stored tuples that conform to the specified signature, null if there are none such. CONTRACT: do not | ||
44 | * modify! | ||
45 | */ | ||
46 | public Collection<Tuple> get(Tuple signature); | ||
47 | |||
48 | /** | ||
49 | * @since 2.4 | ||
50 | */ | ||
51 | default public Map<Tuple, Timeline<Timestamp>> getTimeline(Tuple signature) { | ||
52 | throw new UnsupportedOperationException(); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * This indexer will be updated whenever a Rete update is sent to the active node (or an equivalent time slot | ||
57 | * allotted to it). The active node is typically the indexer itself, but it can be a different node such as its | ||
58 | * parent. | ||
59 | * | ||
60 | * @return the active node that operates this indexer | ||
61 | */ | ||
62 | public Node getActiveNode(); | ||
63 | |||
64 | |||
65 | public Collection<IndexerListener> getListeners(); | ||
66 | |||
67 | public void attachListener(IndexerListener listener); | ||
68 | |||
69 | public void detachListener(IndexerListener listener); | ||
70 | |||
71 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java new file mode 100644 index 00000000..f52b6a06 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java | |||
@@ -0,0 +1,41 @@ | |||
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.rete.index; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
14 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
15 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
16 | |||
17 | /** | ||
18 | * A listener for update events concerning an Indexer. | ||
19 | * | ||
20 | * @author Gabor Bergmann | ||
21 | * | ||
22 | */ | ||
23 | public interface IndexerListener { | ||
24 | /** | ||
25 | * Notifies recipient that the indexer has just received an update. Contract: indexer already reflects the updated | ||
26 | * state. | ||
27 | * | ||
28 | * @param direction | ||
29 | * the direction of the update. | ||
30 | * @param updateElement | ||
31 | * the tuple that was updated. | ||
32 | * @param signature | ||
33 | * the signature of the tuple according to the indexer's mask. | ||
34 | * @param change | ||
35 | * whether this was the first inserted / last revoked update element with this particular signature. | ||
36 | * @since 2.4 | ||
37 | */ | ||
38 | void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp); | ||
39 | |||
40 | Node getOwner(); | ||
41 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java new file mode 100644 index 00000000..a31562e9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java | |||
@@ -0,0 +1,284 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Map; | ||
15 | import java.util.Map.Entry; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; | ||
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.CollectionsFactory.MemoryType; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
23 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
25 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; | ||
26 | import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; | ||
27 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
28 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
29 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
31 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
32 | import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; | ||
33 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
34 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
35 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
36 | |||
37 | /** | ||
38 | * @author Gabor Bergmann | ||
39 | * @author Tamas Szabo | ||
40 | */ | ||
41 | public abstract class IndexerWithMemory extends StandardIndexer | ||
42 | implements Receiver, NetworkStructureChangeSensitiveNode, ResumableNode { | ||
43 | |||
44 | protected MaskedTupleMemory<Timestamp> memory; | ||
45 | |||
46 | /** | ||
47 | * @since 2.3 | ||
48 | */ | ||
49 | protected NetworkStructureChangeSensitiveLogic logic; | ||
50 | |||
51 | /** | ||
52 | * @since 1.6 | ||
53 | */ | ||
54 | protected final Mailbox mailbox; | ||
55 | |||
56 | /** | ||
57 | * @since 2.4 | ||
58 | */ | ||
59 | protected CommunicationGroup group; | ||
60 | |||
61 | public IndexerWithMemory(final ReteContainer reteContainer, final TupleMask mask) { | ||
62 | super(reteContainer, mask); | ||
63 | final boolean isTimely = reteContainer.isTimelyEvaluation() | ||
64 | && reteContainer.getCommunicationTracker().isInRecursiveGroup(this); | ||
65 | memory = MaskedTupleMemory.create(mask, MemoryType.SETS, this, isTimely, isTimely && reteContainer | ||
66 | .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); | ||
67 | reteContainer.registerClearable(memory); | ||
68 | mailbox = instantiateMailbox(); | ||
69 | reteContainer.registerClearable(mailbox); | ||
70 | this.logic = createLogic(); | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public CommunicationGroup getCurrentGroup() { | ||
75 | return this.group; | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public void setCurrentGroup(final CommunicationGroup group) { | ||
80 | this.group = group; | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public void networkStructureChanged() { | ||
85 | super.networkStructureChanged(); | ||
86 | final boolean wasTimely = this.memory.isTimely(); | ||
87 | final boolean isTimely = this.reteContainer.isTimelyEvaluation() | ||
88 | && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this); | ||
89 | if (wasTimely != isTimely) { | ||
90 | final MaskedTupleMemory<Timestamp> newMemory = MaskedTupleMemory.create(mask, MemoryType.SETS, this, | ||
91 | isTimely, isTimely && reteContainer.getTimelyConfiguration() | ||
92 | .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); | ||
93 | newMemory.initializeWith(this.memory, Timestamp.ZERO); | ||
94 | memory.clear(); | ||
95 | memory = newMemory; | ||
96 | } | ||
97 | this.logic = createLogic(); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own | ||
102 | * mailbox implementation. | ||
103 | * | ||
104 | * @return the mailbox | ||
105 | * @since 2.0 | ||
106 | */ | ||
107 | protected Mailbox instantiateMailbox() { | ||
108 | if (this.reteContainer.isTimelyEvaluation()) { | ||
109 | return new TimelyMailbox(this, this.reteContainer); | ||
110 | } else { | ||
111 | return new BehaviorChangingMailbox(this, this.reteContainer); | ||
112 | } | ||
113 | } | ||
114 | |||
115 | @Override | ||
116 | public Mailbox getMailbox() { | ||
117 | return this.mailbox; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * @since 2.0 | ||
122 | */ | ||
123 | public MaskedTupleMemory<Timestamp> getMemory() { | ||
124 | return memory; | ||
125 | } | ||
126 | |||
127 | @Override | ||
128 | public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { | ||
129 | this.logic.update(direction, updateElement, timestamp); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * Refined version of update | ||
134 | * | ||
135 | * @since 2.4 | ||
136 | */ | ||
137 | protected abstract void update(final Direction direction, final Tuple updateElement, final Tuple signature, | ||
138 | final boolean change, final Timestamp timestamp); | ||
139 | |||
140 | @Override | ||
141 | public void appendParent(final Supplier supplier) { | ||
142 | if (parent == null) { | ||
143 | parent = supplier; | ||
144 | } else { | ||
145 | throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent | ||
146 | + ") and cannot connect to additional parent (" + supplier + "). "); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | @Override | ||
151 | public void removeParent(final Supplier supplier) { | ||
152 | if (parent == supplier) { | ||
153 | parent = null; | ||
154 | } else { | ||
155 | throw new IllegalArgumentException( | ||
156 | "Illegal RETE edge removal: the parent of " + this + " is not " + supplier); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * @since 2.4 | ||
162 | */ | ||
163 | @Override | ||
164 | public Collection<Supplier> getParents() { | ||
165 | return Collections.singleton(parent); | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * @since 2.4 | ||
170 | */ | ||
171 | @Override | ||
172 | public void resumeAt(final Timestamp timestamp) { | ||
173 | this.logic.resumeAt(timestamp); | ||
174 | } | ||
175 | |||
176 | /** | ||
177 | * @since 2.4 | ||
178 | */ | ||
179 | @Override | ||
180 | public Timestamp getResumableTimestamp() { | ||
181 | return this.memory.getResumableTimestamp(); | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * @since 2.3 | ||
186 | */ | ||
187 | protected static abstract class NetworkStructureChangeSensitiveLogic { | ||
188 | |||
189 | /** | ||
190 | * @since 2.4 | ||
191 | */ | ||
192 | public abstract void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp); | ||
193 | |||
194 | /** | ||
195 | * @since 2.4 | ||
196 | */ | ||
197 | public abstract void resumeAt(final Timestamp timestamp); | ||
198 | |||
199 | } | ||
200 | |||
201 | /** | ||
202 | * @since 2.3 | ||
203 | */ | ||
204 | protected NetworkStructureChangeSensitiveLogic createLogic() { | ||
205 | if (this.reteContainer.isTimelyEvaluation() | ||
206 | && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
207 | return TIMELY; | ||
208 | } else { | ||
209 | return TIMELESS; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { | ||
214 | |||
215 | @Override | ||
216 | public void resumeAt(final Timestamp timestamp) { | ||
217 | final Iterable<Tuple> signatures = memory.getResumableSignatures(); | ||
218 | |||
219 | final Map<Tuple, Boolean> wasPresent = CollectionsFactory.createMap(); | ||
220 | for (final Tuple signature : signatures) { | ||
221 | wasPresent.put(signature, memory.isPresentAtInfinity(signature)); | ||
222 | } | ||
223 | |||
224 | final Map<Tuple, Map<Tuple, Diff<Timestamp>>> signatureMap = memory.resumeAt(timestamp); | ||
225 | |||
226 | for (final Entry<Tuple, Map<Tuple, Diff<Timestamp>>> outerEntry : signatureMap.entrySet()) { | ||
227 | final Tuple signature = outerEntry.getKey(); | ||
228 | final Map<Tuple, Diff<Timestamp>> diffMap = outerEntry.getValue(); | ||
229 | final boolean isPresent = memory.isPresentAtInfinity(signature); | ||
230 | // only send out a potential true value the first time for a given signature, then set it to false | ||
231 | boolean change = wasPresent.get(signature) ^ isPresent; | ||
232 | |||
233 | for (final Entry<Tuple, Diff<Timestamp>> innerEntry : diffMap.entrySet()) { | ||
234 | final Tuple tuple = innerEntry.getKey(); | ||
235 | final Diff<Timestamp> diffs = innerEntry.getValue(); | ||
236 | for (final Signed<Timestamp> signed : diffs) { | ||
237 | IndexerWithMemory.this.update(signed.getDirection(), tuple, signature, change, | ||
238 | signed.getPayload()); | ||
239 | } | ||
240 | // change is a signature-wise flag, so it is ok to "try" to signal it for the first tuple only | ||
241 | change = false; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | final Timestamp nextTimestamp = memory.getResumableTimestamp(); | ||
246 | if (nextTimestamp != null) { | ||
247 | group.notifyHasMessage(mailbox, nextTimestamp); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | @Override | ||
252 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
253 | final Tuple signature = mask.transform(update); | ||
254 | final boolean wasPresent = memory.isPresentAtInfinity(signature); | ||
255 | final Diff<Timestamp> resultDiff = direction == Direction.INSERT | ||
256 | ? memory.addWithTimestamp(update, signature, timestamp) | ||
257 | : memory.removeWithTimestamp(update, signature, timestamp); | ||
258 | final boolean isPresent = memory.isPresentAtInfinity(signature); | ||
259 | final boolean change = wasPresent ^ isPresent; | ||
260 | for (final Signed<Timestamp> signed : resultDiff) { | ||
261 | IndexerWithMemory.this.update(signed.getDirection(), update, signature, change, signed.getPayload()); | ||
262 | } | ||
263 | } | ||
264 | |||
265 | }; | ||
266 | |||
267 | private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { | ||
268 | |||
269 | @Override | ||
270 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
271 | final Tuple signature = mask.transform(update); | ||
272 | final boolean change = direction == Direction.INSERT ? memory.add(update, signature) | ||
273 | : memory.remove(update, signature); | ||
274 | IndexerWithMemory.this.update(direction, update, signature, change, timestamp); | ||
275 | } | ||
276 | |||
277 | @Override | ||
278 | public void resumeAt(final Timestamp timestamp) { | ||
279 | // there is nothing to resume in the timeless case because we do not even care about timestamps | ||
280 | } | ||
281 | |||
282 | }; | ||
283 | |||
284 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java new file mode 100644 index 00000000..d6f8ef05 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java | |||
@@ -0,0 +1,34 @@ | |||
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.rete.index; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | |||
14 | /** | ||
15 | * An indexer that allows the iteration of all retrievable tuple groups (or reduced groups). | ||
16 | * | ||
17 | * @author Gabor Bergmann | ||
18 | * | ||
19 | */ | ||
20 | public interface IterableIndexer extends Indexer, Iterable<Tuple> { | ||
21 | |||
22 | /** | ||
23 | * A view consisting of exactly those signatures whose tuple group is not empty | ||
24 | * @since 2.0 | ||
25 | */ | ||
26 | public Iterable<Tuple> getSignatures(); | ||
27 | |||
28 | /** | ||
29 | * @return the number of signatures whose tuple group is not empty | ||
30 | * @since 2.0 | ||
31 | */ | ||
32 | public int getBucketCount(); | ||
33 | |||
34 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java new file mode 100644 index 00000000..9a6a0de9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java | |||
@@ -0,0 +1,193 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | |||
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.Direction; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
20 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
21 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
22 | |||
23 | /** | ||
24 | * @author Gabor Bergmann | ||
25 | * | ||
26 | */ | ||
27 | public class JoinNode extends DualInputNode { | ||
28 | |||
29 | public JoinNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) { | ||
30 | super(reteContainer, complementerSecondaryMask); | ||
31 | this.logic = createLogic(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Tuple calibrate(final Tuple primary, final Tuple secondary) { | ||
36 | return unify(primary, secondary); | ||
37 | } | ||
38 | |||
39 | private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { | ||
40 | |||
41 | @Override | ||
42 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
43 | throw new UnsupportedOperationException(); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
48 | if (primarySlot == null || secondarySlot == null) { | ||
49 | return; | ||
50 | } | ||
51 | |||
52 | if (flush) { | ||
53 | reteContainer.flushUpdates(); | ||
54 | } | ||
55 | |||
56 | for (final Tuple signature : primarySlot.getSignatures()) { | ||
57 | // primaries can not be null due to the contract of IterableIndex.getSignatures() | ||
58 | final Collection<Tuple> primaries = primarySlot.get(signature); | ||
59 | final Collection<Tuple> opposites = secondarySlot.get(signature); | ||
60 | if (opposites != null) { | ||
61 | for (final Tuple primary : primaries) { | ||
62 | for (final Tuple opposite : opposites) { | ||
63 | collector.add(unify(primary, opposite)); | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, | ||
72 | final Tuple signature, final boolean change, final Timestamp timestamp) { | ||
73 | // in the default case, all timestamps must be zero | ||
74 | assert Timestamp.ZERO.equals(timestamp); | ||
75 | |||
76 | final Collection<Tuple> opposites = retrieveOpposites(side, signature); | ||
77 | |||
78 | if (!coincidence) { | ||
79 | if (opposites != null) { | ||
80 | for (final Tuple opposite : opposites) { | ||
81 | propagateUpdate(direction, unify(side, updateElement, opposite), timestamp); | ||
82 | } | ||
83 | } | ||
84 | } else { | ||
85 | // compensate for coincidence of slots - this is the case when an Indexer is joined with itself | ||
86 | if (opposites != null) { | ||
87 | for (final Tuple opposite : opposites) { | ||
88 | if (opposite.equals(updateElement)) { | ||
89 | // handle self-joins of a single tuple separately | ||
90 | continue; | ||
91 | } | ||
92 | propagateUpdate(direction, unify(opposite, updateElement), timestamp); | ||
93 | propagateUpdate(direction, unify(updateElement, opposite), timestamp); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | // handle self-joins here | ||
98 | propagateUpdate(direction, unify(updateElement, updateElement), timestamp); | ||
99 | } | ||
100 | } | ||
101 | }; | ||
102 | |||
103 | private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { | ||
104 | |||
105 | @Override | ||
106 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
107 | if (primarySlot == null || secondarySlot == null) { | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | if (flush) { | ||
112 | reteContainer.flushUpdates(); | ||
113 | } | ||
114 | |||
115 | for (final Tuple signature : primarySlot.getSignatures()) { | ||
116 | // primaries can not be null due to the contract of IterableIndex.getSignatures() | ||
117 | final Map<Tuple, Timeline<Timestamp>> primaries = getTimeline(signature, primarySlot); | ||
118 | final Map<Tuple, Timeline<Timestamp>> opposites = getTimeline(signature, secondarySlot); | ||
119 | if (opposites != null) { | ||
120 | for (final Tuple primary : primaries.keySet()) { | ||
121 | for (final Tuple opposite : opposites.keySet()) { | ||
122 | final Timeline<Timestamp> primaryTimeline = primaries.get(primary); | ||
123 | final Timeline<Timestamp> oppositeTimeline = opposites.get(opposite); | ||
124 | final Timeline<Timestamp> mergedTimeline = primaryTimeline | ||
125 | .mergeMultiplicative(oppositeTimeline); | ||
126 | if (!mergedTimeline.isEmpty()) { | ||
127 | collector.put(unify(primary, opposite), mergedTimeline); | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
137 | JoinNode.this.TIMELESS.pullInto(collector, flush); | ||
138 | } | ||
139 | |||
140 | @Override | ||
141 | public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, | ||
142 | final Tuple signature, final boolean change, final Timestamp timestamp) { | ||
143 | final Indexer oppositeIndexer = getSlot(side.opposite()); | ||
144 | final Map<Tuple, Timeline<Timestamp>> opposites = getTimeline(signature, oppositeIndexer); | ||
145 | |||
146 | if (!coincidence) { | ||
147 | if (opposites != null) { | ||
148 | for (final Tuple opposite : opposites.keySet()) { | ||
149 | final Tuple unifiedTuple = unify(side, updateElement, opposite); | ||
150 | for (final Signed<Timestamp> signed : opposites.get(opposite).asChangeSequence()) { | ||
151 | // TODO only consider signed timestamps that are greater or equal to timestamp | ||
152 | // plus compact the previous timestamps into at most one update | ||
153 | propagateUpdate(signed.getDirection().multiply(direction), unifiedTuple, | ||
154 | timestamp.max(signed.getPayload())); | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | } else { | ||
159 | // compensate for coincidence of slots - this is the case when an Indexer is joined with itself | ||
160 | if (opposites != null) { | ||
161 | for (final Tuple opposite : opposites.keySet()) { | ||
162 | if (opposite.equals(updateElement)) { | ||
163 | // handle self-joins of a single tuple separately | ||
164 | continue; | ||
165 | } | ||
166 | final Tuple u1 = unify(opposite, updateElement); | ||
167 | final Tuple u2 = unify(updateElement, opposite); | ||
168 | for (final Signed<Timestamp> oppositeSigned : opposites.get(opposite).asChangeSequence()) { | ||
169 | final Direction updateDirection = direction.multiply(oppositeSigned.getDirection()); | ||
170 | final Timestamp updateTimestamp = timestamp.max(oppositeSigned.getPayload()); | ||
171 | propagateUpdate(updateDirection, u1, updateTimestamp); | ||
172 | propagateUpdate(updateDirection, u2, updateTimestamp); | ||
173 | } | ||
174 | } | ||
175 | } | ||
176 | |||
177 | // handle self-join here | ||
178 | propagateUpdate(direction, unify(updateElement, updateElement), timestamp); | ||
179 | } | ||
180 | } | ||
181 | }; | ||
182 | |||
183 | @Override | ||
184 | protected NetworkStructureChangeSensitiveLogic createTimelessLogic() { | ||
185 | return this.TIMELESS; | ||
186 | } | ||
187 | |||
188 | @Override | ||
189 | protected NetworkStructureChangeSensitiveLogic createTimelyLogic() { | ||
190 | return this.TIMELY; | ||
191 | } | ||
192 | |||
193 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java new file mode 100644 index 00000000..59b75c33 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java | |||
@@ -0,0 +1,55 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
17 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
18 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
19 | |||
20 | /** | ||
21 | * Defines a trivial indexer that identically projects the contents of a memory-equipped node, and can therefore save | ||
22 | * space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents | ||
23 | * directly! | ||
24 | * | ||
25 | * @noimplement Rely on the provided implementations | ||
26 | * @noreference Use only via standard Node and Indexer interfaces | ||
27 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
28 | * @author Gabor Bergmann | ||
29 | */ | ||
30 | |||
31 | public class MemoryIdentityIndexer extends IdentityIndexer { | ||
32 | |||
33 | protected final Collection<Tuple> memory; | ||
34 | |||
35 | /** | ||
36 | * @param reteContainer | ||
37 | * @param tupleWidth | ||
38 | * the width of the tuples of memoryNode | ||
39 | * @param memory | ||
40 | * the memory whose contents are to be identity-indexed | ||
41 | * @param parent | ||
42 | * the parent node that owns the memory | ||
43 | */ | ||
44 | public MemoryIdentityIndexer(ReteContainer reteContainer, int tupleWidth, Collection<Tuple> memory, | ||
45 | Supplier parent, Receiver activeNode, List<ListenerSubscription> sharedSubscriptionList) { | ||
46 | super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); | ||
47 | this.memory = memory; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | protected Collection<Tuple> getTuples() { | ||
52 | return this.memory; | ||
53 | } | ||
54 | |||
55 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java new file mode 100644 index 00000000..204fe433 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java | |||
@@ -0,0 +1,54 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
17 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
18 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
19 | |||
20 | /** | ||
21 | * Defines a trivial indexer that projects the contents of a memory-equipped node to the empty tuple, and can therefore | ||
22 | * save space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents | ||
23 | * directly! | ||
24 | * | ||
25 | * @author Gabor Bergmann | ||
26 | * @noimplement Rely on the provided implementations | ||
27 | * @noreference Use only via standard Node and Indexer interfaces | ||
28 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
29 | */ | ||
30 | public class MemoryNullIndexer extends NullIndexer { | ||
31 | |||
32 | Collection<Tuple> memory; | ||
33 | |||
34 | /** | ||
35 | * @param reteContainer | ||
36 | * @param tupleWidth | ||
37 | * the width of the tuples of memoryNode | ||
38 | * @param memory | ||
39 | * the memory whose contents are to be null-indexed | ||
40 | * @param parent | ||
41 | * the parent node that owns the memory | ||
42 | */ | ||
43 | public MemoryNullIndexer(ReteContainer reteContainer, int tupleWidth, Collection<Tuple> memory, | ||
44 | Supplier parent, Receiver activeNode, List<ListenerSubscription> sharedSubscriptionList) { | ||
45 | super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); | ||
46 | this.memory = memory; | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | protected Collection<Tuple> getTuples() { | ||
51 | return this.memory; | ||
52 | } | ||
53 | |||
54 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java new file mode 100644 index 00000000..a875b29f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2012 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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Iterator; | ||
15 | import java.util.List; | ||
16 | |||
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.Direction; | ||
21 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
22 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
23 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
25 | |||
26 | /** | ||
27 | * Defines an abstract trivial indexer that projects the contents of some stateful node to the empty tuple, and can | ||
28 | * therefore save space. Can only exist in connection with a stateful store, and must be operated by another node (the | ||
29 | * active node). Do not attach parents directly! | ||
30 | * | ||
31 | * @author Gabor Bergmann | ||
32 | * @noimplement Rely on the provided implementations | ||
33 | * @noreference Use only via standard Node and Indexer interfaces | ||
34 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
35 | */ | ||
36 | public abstract class NullIndexer extends SpecializedProjectionIndexer { | ||
37 | |||
38 | protected abstract Collection<Tuple> getTuples(); | ||
39 | |||
40 | protected static final Tuple nullSignature = Tuples.staticArityFlatTupleOf(); | ||
41 | protected static final Collection<Tuple> nullSingleton = Collections.singleton(nullSignature); | ||
42 | protected static final Collection<Tuple> emptySet = Collections.emptySet(); | ||
43 | |||
44 | public NullIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent, Node activeNode, | ||
45 | List<ListenerSubscription> sharedSubscriptionList) { | ||
46 | super(reteContainer, TupleMask.linear(0, tupleWidth), parent, activeNode, sharedSubscriptionList); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public Collection<Tuple> get(Tuple signature) { | ||
51 | if (nullSignature.equals(signature)) | ||
52 | return isEmpty() ? null : getTuples(); | ||
53 | else | ||
54 | return null; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public Collection<Tuple> getSignatures() { | ||
59 | return isEmpty() ? emptySet : nullSingleton; | ||
60 | } | ||
61 | |||
62 | protected boolean isEmpty() { | ||
63 | return getTuples().isEmpty(); | ||
64 | } | ||
65 | |||
66 | protected boolean isSingleElement() { | ||
67 | return getTuples().size() == 1; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public Iterator<Tuple> iterator() { | ||
72 | return getTuples().iterator(); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public int getBucketCount() { | ||
77 | return getTuples().isEmpty() ? 0 : 1; | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, | ||
82 | Timestamp timestamp) { | ||
83 | boolean radical = (direction == Direction.DELETE && isEmpty()) | ||
84 | || (direction == Direction.INSERT && isSingleElement()); | ||
85 | listener.notifyIndexerUpdate(direction, updateElement, nullSignature, radical, timestamp); | ||
86 | } | ||
87 | |||
88 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java new file mode 100644 index 00000000..fef84bb1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java | |||
@@ -0,0 +1,47 @@ | |||
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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
16 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
17 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
18 | |||
19 | /** | ||
20 | * @author Gabor Bergmann Indexer whose lifetime last until the first get() DO NOT connect to nodes! | ||
21 | */ | ||
22 | public class OnetimeIndexer extends GenericProjectionIndexer { | ||
23 | |||
24 | public OnetimeIndexer(ReteContainer reteContainer, TupleMask mask) { | ||
25 | super(reteContainer, mask); | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public Collection<Tuple> get(Tuple signature) { | ||
30 | if (tools.refinery.viatra.runtime.rete.util.Options.releaseOnetimeIndexers) { | ||
31 | reteContainer.unregisterClearable(memory); | ||
32 | reteContainer.unregisterNode(this); | ||
33 | } | ||
34 | return super.get(signature); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public void appendParent(Supplier supplier) { | ||
39 | throw new UnsupportedOperationException("onetime indexer cannot have parents"); | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public void attachListener(IndexerListener listener) { | ||
44 | throw new UnsupportedOperationException("onetime indexer cannot have listeners"); | ||
45 | } | ||
46 | |||
47 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java new file mode 100644 index 00000000..58e593d9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java | |||
@@ -0,0 +1,21 @@ | |||
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.rete.index; | ||
11 | |||
12 | /** | ||
13 | * An iterable indexer that receives updates from a node, and groups received tuples intact, i.e. it does not reduce | ||
14 | * tuple groups. | ||
15 | * | ||
16 | * @author Gabor Bergmann | ||
17 | * | ||
18 | */ | ||
19 | public interface ProjectionIndexer extends IterableIndexer { | ||
20 | |||
21 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java new file mode 100644 index 00000000..9c647aa9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java | |||
@@ -0,0 +1,176 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2012 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.rete.index; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.List; | ||
14 | import java.util.Objects; | ||
15 | |||
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.Direction; | ||
19 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
20 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
21 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
24 | |||
25 | /** | ||
26 | * A specialized projection indexer that can be memory-less (relying on an external source of information). | ||
27 | * | ||
28 | * <p> | ||
29 | * All specialized projection indexers of a single node will share the same listener list, so that notification order is | ||
30 | * maintained (see Bug 518434). | ||
31 | * | ||
32 | * @author Gabor Bergmann | ||
33 | * @noimplement Rely on the provided implementations | ||
34 | * @noreference Use only via standard Node and Indexer interfaces | ||
35 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
36 | */ | ||
37 | public abstract class SpecializedProjectionIndexer extends StandardIndexer implements ProjectionIndexer { | ||
38 | |||
39 | protected Node activeNode; | ||
40 | protected List<ListenerSubscription> subscriptions; | ||
41 | |||
42 | /** | ||
43 | * @since 1.7 | ||
44 | */ | ||
45 | public SpecializedProjectionIndexer(final ReteContainer reteContainer, final TupleMask mask, final Supplier parent, | ||
46 | final Node activeNode, final List<ListenerSubscription> subscriptions) { | ||
47 | super(reteContainer, mask); | ||
48 | this.parent = parent; | ||
49 | this.activeNode = activeNode; | ||
50 | this.subscriptions = subscriptions; | ||
51 | } | ||
52 | |||
53 | public List<ListenerSubscription> getSubscriptions() { | ||
54 | return subscriptions; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public Node getActiveNode() { | ||
59 | return activeNode; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | protected void propagate(final Direction direction, final Tuple updateElement, final Tuple signature, | ||
64 | final boolean change, final Timestamp timestamp) { | ||
65 | throw new UnsupportedOperationException(); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public void attachListener(final IndexerListener listener) { | ||
70 | super.attachListener(listener); | ||
71 | final CommunicationTracker tracker = this.getCommunicationTracker(); | ||
72 | final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener); | ||
73 | final ListenerSubscription subscription = new ListenerSubscription(this, proxy); | ||
74 | tracker.registerDependency(this, proxy.getOwner()); | ||
75 | // See Bug 518434 | ||
76 | // Must add to the first position, so that the later listeners are notified earlier. | ||
77 | // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite | ||
78 | // slot, | ||
79 | // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier, | ||
80 | // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation | ||
81 | subscriptions.add(0, subscription); | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public void detachListener(final IndexerListener listener) { | ||
86 | final CommunicationTracker tracker = this.getCommunicationTracker(); | ||
87 | // obtain the proxy before the super call would unregister the dependency | ||
88 | final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener); | ||
89 | super.detachListener(listener); | ||
90 | final ListenerSubscription subscription = new ListenerSubscription(this, proxy); | ||
91 | final boolean wasContained = subscriptions.remove(subscription); | ||
92 | assert wasContained; | ||
93 | tracker.unregisterDependency(this, proxy.getOwner()); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public void networkStructureChanged() { | ||
98 | super.networkStructureChanged(); | ||
99 | final List<ListenerSubscription> oldSubscriptions = new ArrayList<ListenerSubscription>(); | ||
100 | oldSubscriptions.addAll(subscriptions); | ||
101 | subscriptions.clear(); | ||
102 | for (final ListenerSubscription oldSubscription : oldSubscriptions) { | ||
103 | // there is no need to unregister and re-register the dependency between indexer and listener | ||
104 | // because the owner of the listener is the same (even if it is proxified) | ||
105 | final CommunicationTracker tracker = this.getCommunicationTracker(); | ||
106 | // the subscriptions are shared, so we MUST reuse the indexer of the subscription instead of simply 'this' | ||
107 | final IndexerListener proxy = tracker.proxifyIndexerListener(oldSubscription.indexer, oldSubscription.listener); | ||
108 | final ListenerSubscription newSubscription = new ListenerSubscription(oldSubscription.indexer, proxy); | ||
109 | subscriptions.add(newSubscription); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * @since 2.4 | ||
115 | */ | ||
116 | public abstract void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, | ||
117 | Timestamp timestamp); | ||
118 | |||
119 | /** | ||
120 | * Infrastructure to share subscriptions between specialized indexers of the same parent node. | ||
121 | * | ||
122 | * @author Gabor Bergmann | ||
123 | * @since 1.7 | ||
124 | */ | ||
125 | public static class ListenerSubscription { | ||
126 | protected SpecializedProjectionIndexer indexer; | ||
127 | protected IndexerListener listener; | ||
128 | |||
129 | public ListenerSubscription(SpecializedProjectionIndexer indexer, IndexerListener listener) { | ||
130 | super(); | ||
131 | this.indexer = indexer; | ||
132 | this.listener = listener; | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * @since 2.4 | ||
137 | */ | ||
138 | public SpecializedProjectionIndexer getIndexer() { | ||
139 | return indexer; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * @since 2.4 | ||
144 | */ | ||
145 | public IndexerListener getListener() { | ||
146 | return listener; | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * Call this from parent node. | ||
151 | * @since 2.4 | ||
152 | */ | ||
153 | public void propagate(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
154 | indexer.propagateToListener(listener, direction, updateElement, timestamp); | ||
155 | } | ||
156 | |||
157 | @Override | ||
158 | public int hashCode() { | ||
159 | return Objects.hash(indexer, listener); | ||
160 | } | ||
161 | |||
162 | @Override | ||
163 | public boolean equals(Object obj) { | ||
164 | if (this == obj) | ||
165 | return true; | ||
166 | if (obj == null) | ||
167 | return false; | ||
168 | if (getClass() != obj.getClass()) | ||
169 | return false; | ||
170 | ListenerSubscription other = (ListenerSubscription) obj; | ||
171 | return Objects.equals(listener, other.listener) && Objects.equals(indexer, other.indexer); | ||
172 | } | ||
173 | |||
174 | } | ||
175 | |||
176 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java new file mode 100644 index 00000000..9847a8dd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.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.rete.index; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | |||
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.Direction; | ||
19 | import tools.refinery.viatra.runtime.rete.network.BaseNode; | ||
20 | import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
24 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
25 | |||
26 | /** | ||
27 | * An abstract standard implementation of the Indexer interface, providing common bookkeeping functionality. | ||
28 | * | ||
29 | * @author Gabor Bergmann | ||
30 | * | ||
31 | */ | ||
32 | public abstract class StandardIndexer extends BaseNode implements Indexer, NetworkStructureChangeSensitiveNode { | ||
33 | |||
34 | protected Supplier parent; | ||
35 | private final List<IndexerListener> originalListeners; | ||
36 | private final List<IndexerListener> proxyListeners; | ||
37 | protected TupleMask mask; | ||
38 | |||
39 | public StandardIndexer(ReteContainer reteContainer, TupleMask mask) { | ||
40 | super(reteContainer); | ||
41 | this.parent = null; | ||
42 | this.mask = mask; | ||
43 | this.originalListeners = CollectionsFactory.createObserverList(); | ||
44 | this.proxyListeners = CollectionsFactory.createObserverList(); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @since 2.4 | ||
49 | */ | ||
50 | protected void propagate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) { | ||
51 | for (IndexerListener listener : proxyListeners) { | ||
52 | listener.notifyIndexerUpdate(direction, updateElement, signature, change, timestamp); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public TupleMask getMask() { | ||
58 | return mask; | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public Supplier getParent() { | ||
63 | return parent; | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public void attachListener(IndexerListener listener) { | ||
68 | this.getCommunicationTracker().registerDependency(this, listener.getOwner()); | ||
69 | // obtain the proxy after registering the dependency because then the proxy reflects the new SCC structure | ||
70 | final IndexerListener proxy = this.getCommunicationTracker().proxifyIndexerListener(this, listener); | ||
71 | // See Bug 518434 | ||
72 | // Must add to the first position, so that the later listeners are notified earlier. | ||
73 | // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite slot, | ||
74 | // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier, | ||
75 | // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation | ||
76 | this.originalListeners.add(0, listener); | ||
77 | this.proxyListeners.add(0, proxy); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public void detachListener(IndexerListener listener) { | ||
82 | this.originalListeners.remove(listener); | ||
83 | IndexerListener listenerToRemove = null; | ||
84 | for (final IndexerListener proxyListener : this.proxyListeners) { | ||
85 | if (proxyListener.getOwner() == listener.getOwner()) { | ||
86 | listenerToRemove = proxyListener; | ||
87 | break; | ||
88 | } | ||
89 | } | ||
90 | assert listenerToRemove != null; | ||
91 | this.proxyListeners.remove(listenerToRemove); | ||
92 | this.getCommunicationTracker().unregisterDependency(this, listener.getOwner()); | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public void networkStructureChanged() { | ||
97 | this.proxyListeners.clear(); | ||
98 | for (final IndexerListener original : this.originalListeners) { | ||
99 | this.proxyListeners.add(this.getCommunicationTracker().proxifyIndexerListener(this, original)); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public Collection<IndexerListener> getListeners() { | ||
105 | return proxyListeners; | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public ReteContainer getContainer() { | ||
110 | return reteContainer; | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | protected String toStringCore() { | ||
115 | return super.toStringCore() + "(" + parent + "/" + mask + ")"; | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public void assignTraceInfo(TraceInfo traceInfo) { | ||
120 | super.assignTraceInfo(traceInfo); | ||
121 | if (traceInfo.propagateFromIndexerToSupplierParent()) | ||
122 | if (parent != null) | ||
123 | parent.acceptPropagatedTraceInfo(traceInfo); | ||
124 | } | ||
125 | |||
126 | |||
127 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java new file mode 100644 index 00000000..77202004 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java | |||
@@ -0,0 +1,121 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, 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.rete.index; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.MaskedTuple; | ||
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.Direction; | ||
23 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
24 | import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode; | ||
25 | |||
26 | // UNFINISHED, not used yet | ||
27 | public class TransitiveClosureNodeIndexer extends StandardIndexer implements IterableIndexer { | ||
28 | private TransitiveClosureNode tcNode; | ||
29 | private IncSCCAlg<Object> tcAlg; | ||
30 | private Collection<Tuple> emptySet; | ||
31 | |||
32 | public TransitiveClosureNodeIndexer(TupleMask mask, IncSCCAlg<Object> tcAlg, TransitiveClosureNode tcNode) { | ||
33 | super(tcNode.getContainer(), mask); | ||
34 | this.tcAlg = tcAlg; | ||
35 | this.tcNode = tcNode; | ||
36 | this.emptySet = Collections.emptySet(); | ||
37 | this.parent = tcNode; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Collection<Tuple> get(Tuple signature) { | ||
42 | if (signature.getSize() == mask.sourceWidth) { | ||
43 | if (mask.indices.length == 0) { | ||
44 | // mask ()/2 | ||
45 | return getSignatures(); | ||
46 | } else if (mask.indices.length == 1) { | ||
47 | Set<Tuple> retSet = CollectionsFactory.createSet(); | ||
48 | |||
49 | // mask (0)/2 | ||
50 | if (mask.indices[0] == 0) { | ||
51 | Object source = signature.get(0); | ||
52 | for (Object target : tcAlg.getAllReachableTargets(source)) { | ||
53 | retSet.add(Tuples.staticArityFlatTupleOf(source, target)); | ||
54 | } | ||
55 | return retSet; | ||
56 | } | ||
57 | // mask (1)/2 | ||
58 | if (mask.indices[0] == 1) { | ||
59 | Object target = signature.get(1); | ||
60 | for (Object source : tcAlg.getAllReachableSources(target)) { | ||
61 | retSet.add(Tuples.staticArityFlatTupleOf(source, target)); | ||
62 | } | ||
63 | return retSet; | ||
64 | } | ||
65 | } else { | ||
66 | // mask (0,1)/2 | ||
67 | if (mask.indices[0] == 0 && mask.indices[1] == 1) { | ||
68 | Object source = signature.get(0); | ||
69 | Object target = signature.get(1); | ||
70 | Tuple singleton = Tuples.staticArityFlatTupleOf(source, target); | ||
71 | return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet); | ||
72 | } | ||
73 | // mask (1,0)/2 | ||
74 | if (mask.indices[0] == 1 && mask.indices[1] == 0) { | ||
75 | Object source = signature.get(1); | ||
76 | Object target = signature.get(0); | ||
77 | Tuple singleton = Tuples.staticArityFlatTupleOf(source, target); | ||
78 | return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | return null; | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public int getBucketCount() { | ||
87 | throw new UnsupportedOperationException(); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public Collection<Tuple> getSignatures() { | ||
92 | return asTupleCollection(tcAlg.getTcRelation()); | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public Iterator<Tuple> iterator() { | ||
97 | return asTupleCollection(tcAlg.getTcRelation()).iterator(); | ||
98 | } | ||
99 | |||
100 | private Collection<Tuple> asTupleCollection( | ||
101 | Collection<tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple<Object>> tuples) { | ||
102 | Set<Tuple> retSet = CollectionsFactory.createSet(); | ||
103 | for (tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple<Object> tuple : tuples) { | ||
104 | retSet.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget())); | ||
105 | } | ||
106 | return retSet; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * @since 2.4 | ||
111 | */ | ||
112 | public void propagate(Direction direction, Tuple updateElement, boolean change) { | ||
113 | propagate(direction, updateElement, new MaskedTuple(updateElement, mask), change, null); | ||
114 | } | ||
115 | |||
116 | @Override | ||
117 | public Receiver getActiveNode() { | ||
118 | return tcNode; | ||
119 | } | ||
120 | |||
121 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java new file mode 100644 index 00000000..4319ee29 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java | |||
@@ -0,0 +1,51 @@ | |||
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.rete.index.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
19 | import tools.refinery.viatra.runtime.rete.index.IdentityIndexer; | ||
20 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
24 | |||
25 | public class TimelyMemoryIdentityIndexer extends IdentityIndexer { | ||
26 | |||
27 | protected final TimelyMemory<Timestamp> memory; | ||
28 | |||
29 | public TimelyMemoryIdentityIndexer(final ReteContainer reteContainer, final int tupleWidth, | ||
30 | final TimelyMemory<Timestamp> memory, final Supplier parent, final Receiver activeNode, | ||
31 | final List<ListenerSubscription> sharedSubscriptionList) { | ||
32 | super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); | ||
33 | this.memory = memory; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) { | ||
38 | final Timeline<Timestamp> timestamp = this.memory.get(signature); | ||
39 | if (timestamp != null) { | ||
40 | return Collections.singletonMap(signature, timestamp); | ||
41 | } else { | ||
42 | return null; | ||
43 | } | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | protected Collection<Tuple> getTuples() { | ||
48 | return this.memory.getTuplesAtInfinity(); | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java new file mode 100644 index 00000000..0386b006 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java | |||
@@ -0,0 +1,49 @@ | |||
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.rete.index.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
18 | import tools.refinery.viatra.runtime.rete.index.NullIndexer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
20 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
21 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
23 | |||
24 | public class TimelyMemoryNullIndexer extends NullIndexer { | ||
25 | |||
26 | protected final TimelyMemory<Timestamp> memory; | ||
27 | |||
28 | public TimelyMemoryNullIndexer(final ReteContainer reteContainer, final int tupleWidth, | ||
29 | final TimelyMemory<Timestamp> memory, final Supplier parent, | ||
30 | final Receiver activeNode, final List<ListenerSubscription> sharedSubscriptionList) { | ||
31 | super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); | ||
32 | this.memory = memory; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Map<Tuple, Timeline<Timestamp>> getTimeline(final Tuple signature) { | ||
37 | if (nullSignature.equals(signature)) { | ||
38 | return isEmpty() ? null : this.memory.asMap(); | ||
39 | } else { | ||
40 | return null; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | protected Collection<Tuple> getTuples() { | ||
46 | return this.memory.getTuplesAtInfinity(); | ||
47 | } | ||
48 | |||
49 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingAlg.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingAlg.java new file mode 100644 index 00000000..199b44b1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.alg.counting; | ||
11 | |||
12 | import java.util.List; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.DFSPathFinder; | ||
16 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder; | ||
17 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.ITcRelation; | ||
18 | import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; | ||
19 | import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalWrapper; | ||
20 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; | ||
21 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver; | ||
22 | import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource; | ||
23 | import tools.refinery.viatra.runtime.rete.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 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingTcRelation.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingTcRelation.java new file mode 100644 index 00000000..474c7461 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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.rete.itc.alg.misc.topsort.TopologicalSorting; | ||
17 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.ITcRelation; | ||
18 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/CountingListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/CountingListener.java new file mode 100644 index 00000000..7d507d82 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.alg.incscc; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.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 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java new file mode 100644 index 00000000..774e55eb --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java | |||
@@ -0,0 +1,609 @@ | |||
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.rete.itc.alg.incscc; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
16 | import tools.refinery.viatra.runtime.rete.itc.alg.counting.CountingAlg; | ||
17 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.DFSPathFinder; | ||
18 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.GraphHelper; | ||
19 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder; | ||
20 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple; | ||
21 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs.BFS; | ||
22 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC; | ||
23 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCCResult; | ||
24 | import tools.refinery.viatra.runtime.rete.itc.alg.util.CollectionHelper; | ||
25 | import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; | ||
26 | import tools.refinery.viatra.runtime.rete.itc.igraph.*; | ||
27 | |||
28 | import java.util.*; | ||
29 | import java.util.Map.Entry; | ||
30 | |||
31 | /** | ||
32 | * Incremental SCC maintenance + counting algorithm. | ||
33 | * | ||
34 | * @author Tamas Szabo | ||
35 | * | ||
36 | * @param <V> | ||
37 | * the type parameter of the nodes in the graph data source | ||
38 | */ | ||
39 | public class IncSCCAlg<V> implements IGraphObserver<V>, ITcDataSource<V> { | ||
40 | |||
41 | public UnionFind<V> sccs; | ||
42 | public IBiDirectionalGraphDataSource<V> gds; | ||
43 | private CountingAlg<V> counting; | ||
44 | private Graph<V> reducedGraph; | ||
45 | private IBiDirectionalGraphDataSource<V> reducedGraphIndexer; | ||
46 | private List<ITcObserver<V>> observers; | ||
47 | private CountingListener<V> countingListener; | ||
48 | |||
49 | public IncSCCAlg(IGraphDataSource<V> graphDataSource) { | ||
50 | |||
51 | if (graphDataSource instanceof IBiDirectionalGraphDataSource<?>) { | ||
52 | gds = (IBiDirectionalGraphDataSource<V>) graphDataSource; | ||
53 | } else { | ||
54 | gds = new IBiDirectionalWrapper<V>(graphDataSource); | ||
55 | } | ||
56 | observers = CollectionsFactory.createObserverList(); | ||
57 | sccs = new UnionFind<V>(); | ||
58 | reducedGraph = new Graph<V>(); | ||
59 | reducedGraphIndexer = new IBiDirectionalWrapper<V>(reducedGraph); | ||
60 | countingListener = new CountingListener<V>(this); | ||
61 | initalizeInternalDataStructures(); | ||
62 | gds.attachObserver(this); | ||
63 | } | ||
64 | |||
65 | private void initalizeInternalDataStructures() { | ||
66 | SCCResult<V> _sccres = SCC.computeSCC(gds); | ||
67 | Set<Set<V>> _sccs = _sccres.getSccs(); | ||
68 | |||
69 | for (Set<V> _set : _sccs) { | ||
70 | sccs.makeSet(_set); | ||
71 | } | ||
72 | |||
73 | // Initalization of the reduced graph | ||
74 | for (V n : sccs.getPartitionHeads()) { | ||
75 | reducedGraph.insertNode(n); | ||
76 | } | ||
77 | |||
78 | for (V source : gds.getAllNodes()) { | ||
79 | final IMemoryView<V> targetNodes = gds.getTargetNodes(source); | ||
80 | for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) { | ||
81 | for (int i = 0; i < entry.getValue(); i++) { | ||
82 | V target = entry.getKey(); | ||
83 | V sourceRoot = sccs.find(source); | ||
84 | V targetRoot = sccs.find(target); | ||
85 | |||
86 | if (!sourceRoot.equals(targetRoot)) { | ||
87 | reducedGraph.insertEdge(sourceRoot, targetRoot); | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | counting = new CountingAlg<V>(reducedGraph); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public void edgeInserted(V source, V target) { | ||
98 | V sourceRoot = sccs.find(source); | ||
99 | V targetRoot = sccs.find(target); | ||
100 | |||
101 | // Different SCC | ||
102 | if (!sourceRoot.equals(targetRoot)) { | ||
103 | |||
104 | // source is reachable from target? | ||
105 | if (counting.isReachable(targetRoot, sourceRoot)) { | ||
106 | |||
107 | Set<V> predecessorRoots = counting.getAllReachableSources(sourceRoot); | ||
108 | Set<V> successorRoots = counting.getAllReachableTargets(targetRoot); | ||
109 | |||
110 | // 1. intersection of source and target roots, these will be in the merged SCC | ||
111 | Set<V> isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots); | ||
112 | isectRoots.add(sourceRoot); | ||
113 | isectRoots.add(targetRoot); | ||
114 | |||
115 | // notifications must be issued before Union-Find modifications | ||
116 | if (observers.size() > 0) { | ||
117 | Set<V> sourceSCCs = createSetNullTolerant(predecessorRoots); | ||
118 | sourceSCCs.add(sourceRoot); | ||
119 | Set<V> targetSCCs = createSetNullTolerant(successorRoots); | ||
120 | targetSCCs.add(targetRoot); | ||
121 | |||
122 | // tracing back to actual nodes | ||
123 | for (V sourceSCC : sourceSCCs) { | ||
124 | targetLoop: for (V targetSCC : targetSCCs) { | ||
125 | if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; | ||
126 | |||
127 | boolean needsNotification = | ||
128 | // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. | ||
129 | // Issue notifications only if there is no self-loop present at the moment | ||
130 | (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper | ||
131 | .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) | ||
132 | || | ||
133 | // Case 2. sourceSCC and targetSCC are different sccs. | ||
134 | (!sourceSCC.equals(targetSCC)); | ||
135 | // if self loop is already present omit the notification | ||
136 | if (needsNotification) { | ||
137 | notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), | ||
138 | Direction.INSERT); | ||
139 | } | ||
140 | } | ||
141 | } | ||
142 | } | ||
143 | |||
144 | // 2. delete edges, nodes | ||
145 | List<V> sourceSCCs = new ArrayList<V>(); | ||
146 | List<V> targetSCCs = new ArrayList<V>(); | ||
147 | |||
148 | for (V r : isectRoots) { | ||
149 | List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(r); | ||
150 | List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(r); | ||
151 | |||
152 | for (V sourceSCC : sourceSCCsOfSCC) { | ||
153 | if (!sourceSCC.equals(r)) { | ||
154 | reducedGraph.deleteEdgeIfExists(sourceSCC, r); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | for (V targetSCC : targetSCCsOfSCC) { | ||
159 | if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) { | ||
160 | reducedGraph.deleteEdgeIfExists(r, targetSCC); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | sourceSCCs.addAll(sourceSCCsOfSCC); | ||
165 | targetSCCs.addAll(targetSCCsOfSCC); | ||
166 | } | ||
167 | |||
168 | for (V r : isectRoots) { | ||
169 | reducedGraph.deleteNode(r); | ||
170 | } | ||
171 | |||
172 | // 3. union | ||
173 | Iterator<V> iterator = isectRoots.iterator(); | ||
174 | V newRoot = iterator.next(); | ||
175 | while (iterator.hasNext()) { | ||
176 | newRoot = sccs.union(newRoot, iterator.next()); | ||
177 | } | ||
178 | |||
179 | // 4. add new node | ||
180 | reducedGraph.insertNode(newRoot); | ||
181 | |||
182 | // 5. add edges | ||
183 | Set<V> containedNodes = sccs.getPartition(newRoot); | ||
184 | |||
185 | for (V sourceSCC : sourceSCCs) { | ||
186 | if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) { | ||
187 | reducedGraph.insertEdge(sourceSCC, newRoot); | ||
188 | } | ||
189 | } | ||
190 | for (V targetSCC : targetSCCs) { | ||
191 | if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) { | ||
192 | reducedGraph.insertEdge(newRoot, targetSCC); | ||
193 | } | ||
194 | } | ||
195 | } else { | ||
196 | if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) { | ||
197 | counting.attachObserver(countingListener); | ||
198 | } | ||
199 | reducedGraph.insertEdge(sourceRoot, targetRoot); | ||
200 | counting.detachObserver(countingListener); | ||
201 | } | ||
202 | } else { | ||
203 | // Notifications about self-loops | ||
204 | if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 | ||
205 | && GraphHelper.getEdgeCount(source, target, gds) == 1) { | ||
206 | notifyTcObservers(source, source, Direction.INSERT); | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | @Override | ||
212 | public void edgeDeleted(V source, V target) { | ||
213 | V sourceRoot = sccs.find(source); | ||
214 | V targetRoot = sccs.find(target); | ||
215 | |||
216 | if (!sourceRoot.equals(targetRoot)) { | ||
217 | if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) { | ||
218 | counting.attachObserver(countingListener); | ||
219 | } | ||
220 | reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot); | ||
221 | counting.detachObserver(countingListener); | ||
222 | } else { | ||
223 | // get the graph for the scc whose root is sourceRoot | ||
224 | Graph<V> g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds); | ||
225 | |||
226 | // if source is not reachable from target anymore | ||
227 | if (!BFS.isReachable(source, target, g)) { | ||
228 | // create copies of the current state before destructive manipulation | ||
229 | Map<V, Integer> reachableSources = CollectionsFactory.createMap(); | ||
230 | for (Entry<V, Integer> entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) { | ||
231 | reachableSources.put(entry.getKey(), entry.getValue()); | ||
232 | } | ||
233 | Map<V, Integer> reachableTargets = CollectionsFactory.createMap(); | ||
234 | for (Entry<V, Integer> entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) { | ||
235 | reachableTargets.put(entry.getKey(), entry.getValue()); | ||
236 | } | ||
237 | |||
238 | SCCResult<V> _newSccs = SCC.computeSCC(g); | ||
239 | |||
240 | // delete scc node (and with its edges too) | ||
241 | for (Entry<V, Integer> entry : reachableSources.entrySet()) { | ||
242 | V s = entry.getKey(); | ||
243 | for (int i = 0; i < entry.getValue(); i++) { | ||
244 | reducedGraph.deleteEdgeIfExists(s, sourceRoot); | ||
245 | } | ||
246 | } | ||
247 | |||
248 | for (Entry<V, Integer> entry : reachableTargets.entrySet()) { | ||
249 | V t = entry.getKey(); | ||
250 | for (int i = 0; i < entry.getValue(); i++) { | ||
251 | reducedGraph.deleteEdgeIfExists(sourceRoot, t); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | sccs.deleteSet(sourceRoot); | ||
256 | reducedGraph.deleteNode(sourceRoot); | ||
257 | |||
258 | Set<Set<V>> newSCCs = _newSccs.getSccs(); | ||
259 | Set<V> newSCCRoots = CollectionsFactory.createSet(); | ||
260 | |||
261 | // add new nodes and edges to the reduced graph | ||
262 | for (Set<V> newSCC : newSCCs) { | ||
263 | V newRoot = sccs.makeSet(newSCC); | ||
264 | reducedGraph.insertNode(newRoot); | ||
265 | newSCCRoots.add(newRoot); | ||
266 | } | ||
267 | for (V newSCCRoot : newSCCRoots) { | ||
268 | List<V> sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot); | ||
269 | List<V> targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot); | ||
270 | |||
271 | for (V sourceSCC : sourceSCCsOfSCC) { | ||
272 | if (!sourceSCC.equals(newSCCRoot)) { | ||
273 | reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot); | ||
274 | } | ||
275 | } | ||
276 | for (V targetSCC : targetSCCsOfSCC) { | ||
277 | if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot)) | ||
278 | reducedGraph.insertEdge(newSCCRoot, targetSCC); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | // Must be after the union-find modifications | ||
283 | if (observers.size() > 0) { | ||
284 | V newSourceRoot = sccs.find(source); | ||
285 | V newTargetRoot = sccs.find(target); | ||
286 | |||
287 | Set<V> sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot)); | ||
288 | sourceSCCs.add(newSourceRoot); | ||
289 | |||
290 | Set<V> targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot)); | ||
291 | targetSCCs.add(newTargetRoot); | ||
292 | |||
293 | for (V sourceSCC : sourceSCCs) { | ||
294 | targetLoop: for (V targetSCC : targetSCCs) { | ||
295 | if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; | ||
296 | |||
297 | boolean needsNotification = | ||
298 | // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. | ||
299 | // Issue notifications only if there is no self-loop present at the moment | ||
300 | (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper | ||
301 | .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) | ||
302 | || | ||
303 | // Case 2. sourceSCC and targetSCC are different sccs. | ||
304 | (!sourceSCC.equals(targetSCC)); | ||
305 | // if self loop is already present omit the notification | ||
306 | if (needsNotification) { | ||
307 | notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), | ||
308 | Direction.DELETE); | ||
309 | } | ||
310 | } | ||
311 | } | ||
312 | } | ||
313 | } else { | ||
314 | // only handle self-loop notifications - sourceRoot equals to targetRoot | ||
315 | if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 | ||
316 | && GraphHelper.getEdgeCount(source, target, gds) == 0) { | ||
317 | notifyTcObservers(source, source, Direction.DELETE); | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | @Override | ||
324 | public void nodeInserted(V n) { | ||
325 | sccs.makeSet(n); | ||
326 | reducedGraph.insertNode(n); | ||
327 | } | ||
328 | |||
329 | @Override | ||
330 | public void nodeDeleted(V n) { | ||
331 | IMemoryView<V> sources = gds.getSourceNodes(n); | ||
332 | IMemoryView<V> targets = gds.getTargetNodes(n); | ||
333 | |||
334 | for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) { | ||
335 | for (int i = 0; i < entry.getValue(); i++) { | ||
336 | V source = entry.getKey(); | ||
337 | edgeDeleted(source, n); | ||
338 | } | ||
339 | } | ||
340 | |||
341 | for (Entry<V, Integer> entry : targets.entriesWithMultiplicities()) { | ||
342 | for (int i = 0; i < entry.getValue(); i++) { | ||
343 | V target = entry.getKey(); | ||
344 | edgeDeleted(n, target); | ||
345 | } | ||
346 | } | ||
347 | |||
348 | sccs.deleteSet(n); | ||
349 | } | ||
350 | |||
351 | @Override | ||
352 | public void attachObserver(ITcObserver<V> to) { | ||
353 | observers.add(to); | ||
354 | } | ||
355 | |||
356 | @Override | ||
357 | public void detachObserver(ITcObserver<V> to) { | ||
358 | observers.remove(to); | ||
359 | } | ||
360 | |||
361 | @Override | ||
362 | public Set<V> getAllReachableTargets(V source) { | ||
363 | V sourceRoot = sccs.find(source); | ||
364 | Set<V> containedNodes = sccs.getPartition(sourceRoot); | ||
365 | Set<V> targets = CollectionsFactory.createSet(); | ||
366 | |||
367 | if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) { | ||
368 | targets.addAll(containedNodes); | ||
369 | } | ||
370 | |||
371 | Set<V> rootSet = counting.getAllReachableTargets(sourceRoot); | ||
372 | if (rootSet != null) { | ||
373 | for (V _root : rootSet) { | ||
374 | targets.addAll(sccs.getPartition(_root)); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | return targets; | ||
379 | } | ||
380 | |||
381 | @Override | ||
382 | public Set<V> getAllReachableSources(V target) { | ||
383 | V targetRoot = sccs.find(target); | ||
384 | Set<V> containedNodes = sccs.getPartition(targetRoot); | ||
385 | Set<V> sources = CollectionsFactory.createSet(); | ||
386 | |||
387 | if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) { | ||
388 | sources.addAll(containedNodes); | ||
389 | } | ||
390 | |||
391 | Set<V> rootSet = counting.getAllReachableSources(targetRoot); | ||
392 | if (rootSet != null) { | ||
393 | for (V _root : rootSet) { | ||
394 | sources.addAll(sccs.getPartition(_root)); | ||
395 | } | ||
396 | } | ||
397 | return sources; | ||
398 | } | ||
399 | |||
400 | @Override | ||
401 | public boolean isReachable(V source, V target) { | ||
402 | V sourceRoot = sccs.find(source); | ||
403 | V targetRoot = sccs.find(target); | ||
404 | |||
405 | if (sourceRoot.equals(targetRoot)) | ||
406 | return true; | ||
407 | else | ||
408 | return counting.isReachable(sourceRoot, targetRoot); | ||
409 | } | ||
410 | |||
411 | public List<V> getReachabilityPath(V source, V target) { | ||
412 | if (!isReachable(source, target)) { | ||
413 | return null; | ||
414 | } else { | ||
415 | Set<V> sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source), | ||
416 | counting.getAllReachableSources(target)); | ||
417 | sccsInSubGraph.add(sccs.find(source)); | ||
418 | sccsInSubGraph.add(sccs.find(target)); | ||
419 | Set<V> nodesInSubGraph = CollectionsFactory.createSet(); | ||
420 | |||
421 | for (V sccRoot : sccsInSubGraph) { | ||
422 | nodesInSubGraph.addAll(sccs.getPartition(sccRoot)); | ||
423 | } | ||
424 | |||
425 | return GraphHelper.constructPath(source, target, nodesInSubGraph, gds); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | /** | ||
430 | * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present | ||
431 | * multiple times in the returned list (multiple edges between the two SCCs). | ||
432 | * | ||
433 | * @param root | ||
434 | * @return the list of reachable target SCCs | ||
435 | */ | ||
436 | private List<V> getSourceSCCsOfSCC(V root) { | ||
437 | List<V> sourceSCCs = new ArrayList<V>(); | ||
438 | |||
439 | for (V containedNode : this.sccs.getPartition(root)) { | ||
440 | IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode); | ||
441 | for (V source : sourceNodes.distinctValues()) { | ||
442 | sourceSCCs.add(this.sccs.find(source)); | ||
443 | } | ||
444 | } | ||
445 | |||
446 | return sourceSCCs; | ||
447 | } | ||
448 | |||
449 | /** | ||
450 | * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph, | ||
451 | * false otherwise (if this SCC is a source in the reduced graph). | ||
452 | * | ||
453 | * @param root the root node of an SCC | ||
454 | * @return true if it has incoming edges, false otherwise | ||
455 | * @since 1.6 | ||
456 | */ | ||
457 | public boolean hasIncomingEdges(final V root) { | ||
458 | for (final V containedNode : this.sccs.getPartition(root)) { | ||
459 | final IMemoryView<V> sourceNodes = this.gds.getSourceNodes(containedNode); | ||
460 | for (final V source : sourceNodes.distinctValues()) { | ||
461 | final V otherRoot = this.sccs.find(source); | ||
462 | if (!Objects.equals(root, otherRoot)) { | ||
463 | return true; | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | return false; | ||
468 | } | ||
469 | |||
470 | /** | ||
471 | * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present | ||
472 | * multiple times in the returned list (multiple edges between the two SCCs). | ||
473 | * | ||
474 | * @param root | ||
475 | * @return the list of reachable target SCCs | ||
476 | */ | ||
477 | private List<V> getTargetSCCsOfSCC(V root) { | ||
478 | List<V> targetSCCs = new ArrayList<V>(); | ||
479 | |||
480 | for (V containedNode : this.sccs.getPartition(root)) { | ||
481 | IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode); | ||
482 | for (V target : targetNodes.distinctValues()) { | ||
483 | targetSCCs.add(this.sccs.find(target)); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | return targetSCCs; | ||
488 | } | ||
489 | |||
490 | /** | ||
491 | * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph, | ||
492 | * false otherwise (if this SCC is a sink in the reduced graph). | ||
493 | * | ||
494 | * @param root the root node of an SCC | ||
495 | * @return true if it has outgoing edges, false otherwise | ||
496 | * @since 1.6 | ||
497 | */ | ||
498 | public boolean hasOutgoingEdges(V root) { | ||
499 | for (final V containedNode : this.sccs.getPartition(root)) { | ||
500 | final IMemoryView<V> targetNodes = this.gds.getTargetNodes(containedNode); | ||
501 | for (final V target : targetNodes.distinctValues()) { | ||
502 | final V otherRoot = this.sccs.find(target); | ||
503 | if (!Objects.equals(root, otherRoot)) { | ||
504 | return true; | ||
505 | } | ||
506 | } | ||
507 | } | ||
508 | return false; | ||
509 | } | ||
510 | |||
511 | @Override | ||
512 | public void dispose() { | ||
513 | gds.detachObserver(this); | ||
514 | counting.dispose(); | ||
515 | } | ||
516 | |||
517 | /** | ||
518 | * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification | ||
519 | * will be the Descartes product of the two sets given. | ||
520 | * | ||
521 | * @param sources | ||
522 | * the source nodes | ||
523 | * @param targets | ||
524 | * the target nodes | ||
525 | * @param direction | ||
526 | */ | ||
527 | protected void notifyTcObservers(Set<V> sources, Set<V> targets, Direction direction) { | ||
528 | for (V s : sources) { | ||
529 | for (V t : targets) { | ||
530 | notifyTcObservers(s, t, direction); | ||
531 | } | ||
532 | } | ||
533 | } | ||
534 | |||
535 | private void notifyTcObservers(V source, V target, Direction direction) { | ||
536 | for (ITcObserver<V> observer : observers) { | ||
537 | if (direction == Direction.INSERT) { | ||
538 | observer.tupleInserted(source, target); | ||
539 | } | ||
540 | if (direction == Direction.DELETE) { | ||
541 | observer.tupleDeleted(source, target); | ||
542 | } | ||
543 | } | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * Returns the node that is selected as the representative of the SCC containing the argument. | ||
548 | * @since 1.6 | ||
549 | */ | ||
550 | public V getRepresentative(V node) { | ||
551 | return sccs.find(node); | ||
552 | } | ||
553 | |||
554 | public Set<Tuple<V>> getTcRelation() { | ||
555 | Set<Tuple<V>> resultSet = new HashSet<Tuple<V>>(); | ||
556 | |||
557 | for (V sourceRoot : sccs.getPartitionHeads()) { | ||
558 | Set<V> sources = sccs.getPartition(sourceRoot); | ||
559 | if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) { | ||
560 | for (V source : sources) { | ||
561 | for (V target : sources) { | ||
562 | resultSet.add(new Tuple<V>(source, target)); | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | |||
567 | Set<V> reachableTargets = counting.getAllReachableTargets(sourceRoot); | ||
568 | if (reachableTargets != null) { | ||
569 | for (V targetRoot : reachableTargets) { | ||
570 | for (V source : sources) { | ||
571 | for (V target : sccs.getPartition(targetRoot)) { | ||
572 | resultSet.add(new Tuple<V>(source, target)); | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | } | ||
577 | } | ||
578 | |||
579 | return resultSet; | ||
580 | } | ||
581 | |||
582 | public boolean isIsolated(V node) { | ||
583 | IMemoryView<V> targets = gds.getTargetNodes(node); | ||
584 | IMemoryView<V> sources = gds.getSourceNodes(node); | ||
585 | return targets.isEmpty() && sources.isEmpty(); | ||
586 | } | ||
587 | |||
588 | @Override | ||
589 | public IGraphPathFinder<V> getPathFinder() { | ||
590 | return new DFSPathFinder<V>(gds, this); | ||
591 | } | ||
592 | |||
593 | /** | ||
594 | * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)}) | ||
595 | * @since 1.6 | ||
596 | */ | ||
597 | public Graph<V> getReducedGraph() { | ||
598 | return reducedGraph; | ||
599 | } | ||
600 | |||
601 | private static <V> Set<V> createSetNullTolerant(Set<V> initial) { | ||
602 | if (initial != null) | ||
603 | return CollectionsFactory.createSet(initial); | ||
604 | else | ||
605 | return CollectionsFactory.createSet(); | ||
606 | } | ||
607 | |||
608 | |||
609 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/DFSPathFinder.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/DFSPathFinder.java new file mode 100644 index 00000000..2cec33a2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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.rete.itc.igraph.IGraphDataSource; | ||
20 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Edge.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Edge.java new file mode 100644 index 00000000..862c99b3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java new file mode 100644 index 00000000..b79e4d45 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java | |||
@@ -0,0 +1,169 @@ | |||
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.rete.itc.alg.misc; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.IMemoryView; | ||
12 | import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; | ||
13 | import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; | ||
14 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; | ||
15 | |||
16 | import java.util.*; | ||
17 | import java.util.Map.Entry; | ||
18 | |||
19 | /** | ||
20 | * Utility class for graph related operations. | ||
21 | * | ||
22 | * @author Tamas Szabo | ||
23 | */ | ||
24 | public class GraphHelper { | ||
25 | |||
26 | private GraphHelper() {/*Utility class constructor*/} | ||
27 | |||
28 | /** | ||
29 | * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes. | ||
30 | * | ||
31 | * @param nodesInSubGraph | ||
32 | * the nodes that are present in the subgraph | ||
33 | * @param graphDataSource | ||
34 | * the graph data source for the original graph | ||
35 | * @return the subgraph associated to the given nodes | ||
36 | */ | ||
37 | public static <V> Graph<V> getSubGraph(Collection<V> nodesInSubGraph, | ||
38 | IBiDirectionalGraphDataSource<V> graphDataSource) { | ||
39 | Graph<V> g = new Graph<V>(); | ||
40 | if (nodesInSubGraph != null) { | ||
41 | for (V node : nodesInSubGraph) { | ||
42 | g.insertNode(node); | ||
43 | } | ||
44 | |||
45 | for (V node : nodesInSubGraph) { | ||
46 | IMemoryView<V> sources = graphDataSource.getSourceNodes(node); | ||
47 | for (Entry<V, Integer> entry : sources.entriesWithMultiplicities()) { | ||
48 | for (int i = 0; i < entry.getValue(); i++) { | ||
49 | V s = entry.getKey(); | ||
50 | if (nodesInSubGraph.contains(s)) { | ||
51 | g.insertEdge(s, node); | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | } | ||
56 | } | ||
57 | |||
58 | return g; | ||
59 | } | ||
60 | |||
61 | /** | ||
62 | * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of | ||
63 | * nodes are used, this way it is possible to construct a path in a given subgraph. | ||
64 | * | ||
65 | * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph | ||
66 | * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times | ||
67 | * in the returned {@link List}. | ||
68 | * | ||
69 | * @param source | ||
70 | * the source node | ||
71 | * @param target | ||
72 | * the target node | ||
73 | * @param nodesInGraph | ||
74 | * the nodes that are present in the subgraph | ||
75 | * @param graphDataSource | ||
76 | * the graph data source | ||
77 | * @return the path between the two nodes | ||
78 | */ | ||
79 | public static <V> List<V> constructPath(V source, V target, Set<V> nodesInGraph, | ||
80 | IGraphDataSource<V> graphDataSource) { | ||
81 | Set<V> visitedNodes = new HashSet<V>(); | ||
82 | List<V> path = new ArrayList<V>(); | ||
83 | |||
84 | visitedNodes.add(source); | ||
85 | path.add(source); | ||
86 | V act = source; | ||
87 | |||
88 | // if source and target are the same node | ||
89 | if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) { | ||
90 | // the node will be present in the path two times | ||
91 | path.add(source); | ||
92 | return path; | ||
93 | } else { | ||
94 | while (act != null) { | ||
95 | V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes); | ||
96 | if (nextNode == null && path.size() > 1) { | ||
97 | // needs to backtrack along path | ||
98 | // remove the last element in the path because we can't go | ||
99 | // anywhere from there | ||
100 | path.remove(path.size() - 1); | ||
101 | while (nextNode == null && path.size() > 0) { | ||
102 | V lastPathElement = path.get(path.size() - 1); | ||
103 | nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes); | ||
104 | if (nextNode == null) { | ||
105 | path.remove(path.size() - 1); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | if (nextNode != null) { | ||
111 | visitedNodes.add(nextNode); | ||
112 | path.add(nextNode); | ||
113 | if (nextNode.equals(target)) { | ||
114 | return path; | ||
115 | } | ||
116 | } | ||
117 | act = nextNode; | ||
118 | } | ||
119 | return null; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | private static <V> V getNextNodeToVisit(V act, IGraphDataSource<V> graphDataSource, Set<V> nodesInSubGraph, | ||
124 | Set<V> visitedNodes) { | ||
125 | IMemoryView<V> targetNodes = graphDataSource.getTargetNodes(act); | ||
126 | for (Entry<V, Integer> entry : targetNodes.entriesWithMultiplicities()) { | ||
127 | for (int i = 0; i < entry.getValue(); i++) { | ||
128 | V node = entry.getKey(); | ||
129 | if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) { | ||
130 | return node; | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | return null; | ||
135 | } | ||
136 | |||
137 | /** | ||
138 | * Returns the number of self-loop edges for the given node. | ||
139 | * | ||
140 | * @param node | ||
141 | * the node | ||
142 | * @param graphDataSource | ||
143 | * the graph data source | ||
144 | * @return the number of self-loop edges | ||
145 | */ | ||
146 | public static <V> int getEdgeCount(V node, IGraphDataSource<V> graphDataSource) { | ||
147 | return getEdgeCount(node, node, graphDataSource); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Returns the number of edges between the given source and target nodes. | ||
152 | * | ||
153 | * @param source | ||
154 | * the source node | ||
155 | * @param target | ||
156 | * the target node | ||
157 | * @param graphDataSource | ||
158 | * the graph data source | ||
159 | * @return the number of parallel edges between the two nodes | ||
160 | */ | ||
161 | public static <V> int getEdgeCount(V source, V target, IGraphDataSource<V> graphDataSource) { | ||
162 | Integer count = graphDataSource.getTargetNodes(source).getCount(target); | ||
163 | if (count == null) { | ||
164 | return 0; | ||
165 | } else { | ||
166 | return count; | ||
167 | } | ||
168 | } | ||
169 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/IGraphPathFinder.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/IGraphPathFinder.java new file mode 100644 index 00000000..624f9f7d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.alg.misc; | ||
10 | |||
11 | import java.util.Deque; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.rete.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 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/ITcRelation.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/ITcRelation.java new file mode 100644 index 00000000..9fd85ae1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Tuple.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Tuple.java new file mode 100644 index 00000000..84c79dcf --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java new file mode 100644 index 00000000..22ce8962 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java | |||
@@ -0,0 +1,148 @@ | |||
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.rete.itc.alg.misc.bfs; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; | ||
13 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; | ||
14 | |||
15 | import java.util.*; | ||
16 | |||
17 | public class BFS<V> { | ||
18 | |||
19 | private BFS() {/*Utility class constructor*/} | ||
20 | |||
21 | /** | ||
22 | * Performs a breadth first search on the given graph to determine whether source is reachable from target. | ||
23 | * | ||
24 | * @param <V> | ||
25 | * the type parameter of the nodes in the graph | ||
26 | * @param source | ||
27 | * the source node | ||
28 | * @param target | ||
29 | * the target node | ||
30 | * @param graph | ||
31 | * the graph data source | ||
32 | * @return true if source is reachable from target, false otherwise | ||
33 | */ | ||
34 | public static <V> boolean isReachable(V source, V target, IGraphDataSource<V> graph) { | ||
35 | Deque<V> nodeQueue = new ArrayDeque<V>(); | ||
36 | Set<V> visited = new HashSet<V>(); | ||
37 | |||
38 | nodeQueue.add(source); | ||
39 | visited.add(source); | ||
40 | |||
41 | boolean ret = _isReachable(target, graph, nodeQueue, visited); | ||
42 | return ret; | ||
43 | } | ||
44 | |||
45 | private static <V> boolean _isReachable(V target, IGraphDataSource<V> graph, Deque<V> nodeQueue, Set<V> visited) { | ||
46 | |||
47 | while (!nodeQueue.isEmpty()) { | ||
48 | V node = nodeQueue.removeFirst(); | ||
49 | for (V t : graph.getTargetNodes(node).distinctValues()){ | ||
50 | if (t.equals(target)) { | ||
51 | return true; | ||
52 | } | ||
53 | if (!visited.contains(t)) { | ||
54 | visited.add(t); | ||
55 | nodeQueue.addLast(t); | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | return false; | ||
60 | } | ||
61 | |||
62 | public static <V> Set<V> reachableSources(IBiDirectionalGraphDataSource<V> graph, V target) { | ||
63 | Set<V> retSet = new HashSet<V>(); | ||
64 | retSet.add(target); | ||
65 | Deque<V> nodeQueue = new ArrayDeque<V>(); | ||
66 | nodeQueue.add(target); | ||
67 | |||
68 | _reachableSources(graph, nodeQueue, retSet); | ||
69 | |||
70 | return retSet; | ||
71 | } | ||
72 | |||
73 | private static <V> void _reachableSources(IBiDirectionalGraphDataSource<V> graph, Deque<V> nodeQueue, | ||
74 | Set<V> retSet) { | ||
75 | while (!nodeQueue.isEmpty()) { | ||
76 | V node = nodeQueue.removeFirst(); | ||
77 | for (V _node : graph.getSourceNodes(node).distinctValues()) { | ||
78 | if (!retSet.contains(_node)) { | ||
79 | retSet.add(_node); | ||
80 | nodeQueue.addLast(_node); | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | |||
86 | public static <V> Set<V> reachableTargets(IGraphDataSource<V> graph, V source) { | ||
87 | Set<V> retSet = new HashSet<V>(); | ||
88 | retSet.add(source); | ||
89 | Deque<V> nodeQueue = new ArrayDeque<V>(); | ||
90 | nodeQueue.add(source); | ||
91 | |||
92 | _reachableTargets(graph, nodeQueue, retSet); | ||
93 | |||
94 | return retSet; | ||
95 | } | ||
96 | |||
97 | private static <V> void _reachableTargets(IGraphDataSource<V> graph, Deque<V> nodeQueue, Set<V> retSet) { | ||
98 | while (!nodeQueue.isEmpty()) { | ||
99 | V node = nodeQueue.removeFirst(); | ||
100 | |||
101 | for (V _node : graph.getTargetNodes(node).distinctValues()) { | ||
102 | |||
103 | if (!retSet.contains(_node)) { | ||
104 | retSet.add(_node); | ||
105 | nodeQueue.addLast(_node); | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Performs a breadth first search on the given graph and collects all the nodes along the path from source to | ||
113 | * target if such path exists. | ||
114 | * | ||
115 | * @param <V> | ||
116 | * the type parameter of the nodes in the graph | ||
117 | * @param source | ||
118 | * the source node | ||
119 | * @param target | ||
120 | * the target node | ||
121 | * @param graph | ||
122 | * the graph data source | ||
123 | * @return the set of nodes along the path | ||
124 | */ | ||
125 | public static <V> Set<V> collectNodesAlongPath(V source, V target, IGraphDataSource<V> graph) { | ||
126 | Set<V> path = new HashSet<V>(); | ||
127 | _collectNodesAlongPath(source, target, graph, path); | ||
128 | return path; | ||
129 | } | ||
130 | |||
131 | private static <V> boolean _collectNodesAlongPath(V node, V target, IGraphDataSource<V> graph, Set<V> path) { | ||
132 | |||
133 | boolean res = false; | ||
134 | |||
135 | // end recursion | ||
136 | if (node.equals(target)) { | ||
137 | path.add(node); | ||
138 | return true; | ||
139 | } else { | ||
140 | for (V _nodeT : graph.getTargetNodes(node).distinctValues()) { | ||
141 | res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res; | ||
142 | } | ||
143 | if (res) | ||
144 | path.add(node); | ||
145 | return res; | ||
146 | } | ||
147 | } | ||
148 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/PKAlg.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/PKAlg.java new file mode 100644 index 00000000..892d048e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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.rete.itc.igraph.IBiDirectionalGraphDataSource; | ||
19 | import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalWrapper; | ||
20 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; | ||
21 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java new file mode 100644 index 00000000..de070839 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java | |||
@@ -0,0 +1,143 @@ | |||
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.rete.itc.alg.misc.scc; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
13 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; | ||
14 | |||
15 | import java.util.*; | ||
16 | |||
17 | /** | ||
18 | * Efficient algorithms to compute the Strongly Connected Components in a directed graph. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * | ||
22 | * @param <V> | ||
23 | * the type parameter of the nodes in the graph | ||
24 | */ | ||
25 | public class SCC<V> { | ||
26 | |||
27 | private SCC() {/*Utility class constructor*/} | ||
28 | |||
29 | public static long sccId = 0; | ||
30 | |||
31 | /** | ||
32 | * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm) | ||
33 | * | ||
34 | * @param g | ||
35 | * the directed graph data source | ||
36 | * @return the set of SCCs | ||
37 | */ | ||
38 | public static <V> SCCResult<V> computeSCC(IGraphDataSource<V> g) { | ||
39 | int index = 0; | ||
40 | Set<Set<V>> ret = new HashSet<Set<V>>(); | ||
41 | |||
42 | // stores the lowlink and index information for the given node | ||
43 | Map<V, SCCProperty> nodeMap = CollectionsFactory.createMap(); | ||
44 | |||
45 | // stores all target nodes of a given node - the list will be modified | ||
46 | Map<V, Set<V>> targetNodeMap = CollectionsFactory.createMap(); | ||
47 | |||
48 | // stores those target nodes for a given node which have not been visited | ||
49 | Map<V, Set<V>> notVisitedMap = CollectionsFactory.createMap(); | ||
50 | |||
51 | // stores the nodes during the traversal | ||
52 | Deque<V> nodeStack = new ArrayDeque<V>(); | ||
53 | |||
54 | // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time) | ||
55 | Deque<V> sccStack = new ArrayDeque<V>(); | ||
56 | |||
57 | boolean sink = false, finishedTraversal = true; | ||
58 | |||
59 | // initialize all nodes with 0 index and 0 lowlink | ||
60 | Set<V> allNodes = g.getAllNodes(); | ||
61 | for (V n : allNodes) { | ||
62 | nodeMap.put(n, new SCCProperty(0, 0)); | ||
63 | } | ||
64 | |||
65 | for (V n : allNodes) { | ||
66 | // if the node has not been visited yet | ||
67 | if (nodeMap.get(n).getIndex() == 0) { | ||
68 | nodeStack.push(n); | ||
69 | |||
70 | while (!nodeStack.isEmpty()) { | ||
71 | V currentNode = nodeStack.peekLast(); | ||
72 | sink = false; | ||
73 | finishedTraversal = false; | ||
74 | SCCProperty prop = nodeMap.get(currentNode); | ||
75 | |||
76 | if (nodeMap.get(currentNode).getIndex() == 0) { | ||
77 | index++; | ||
78 | sccStack.addLast(currentNode); | ||
79 | prop.setIndex(index); | ||
80 | prop.setLowlink(index); | ||
81 | |||
82 | notVisitedMap.put(currentNode, new HashSet<V>()); | ||
83 | |||
84 | // storing the target nodes of the actual node | ||
85 | if (g.getTargetNodes(currentNode) != null) { | ||
86 | Set<V> targets = g.getTargetNodes(currentNode).distinctValues(); | ||
87 | targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets)); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if (targetNodeMap.get(currentNode) != null) { | ||
92 | |||
93 | // remove node from stack, the exploration of its children has finished | ||
94 | if (targetNodeMap.get(currentNode).size() == 0) { | ||
95 | targetNodeMap.remove(currentNode); | ||
96 | |||
97 | nodeStack.removeLast(); | ||
98 | |||
99 | for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) { | ||
100 | if (notVisitedMap.get(currentNode).contains(targetNode)) { | ||
101 | prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink())); | ||
102 | } else if (sccStack.contains(targetNode)) { | ||
103 | prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex())); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | finishedTraversal = true; | ||
108 | } else { | ||
109 | V targetNode = targetNodeMap.get(currentNode).iterator().next(); | ||
110 | targetNodeMap.get(currentNode).remove(targetNode); | ||
111 | // if the targetNode has not yet been visited push it to the stack | ||
112 | // and mark it in the notVisitedMap | ||
113 | if (nodeMap.get(targetNode).getIndex() == 0) { | ||
114 | notVisitedMap.get(currentNode).add(targetNode); | ||
115 | nodeStack.addLast(targetNode); | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | // if currentNode has no target nodes | ||
120 | else { | ||
121 | nodeStack.removeLast(); | ||
122 | sink = true; | ||
123 | } | ||
124 | |||
125 | // create scc if node is a sink or an scc has been found | ||
126 | if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) { | ||
127 | Set<V> sc = new HashSet<V>(); | ||
128 | V targetNode = null; | ||
129 | |||
130 | do { | ||
131 | targetNode = sccStack.removeLast(); | ||
132 | sc.add(targetNode); | ||
133 | } while (!targetNode.equals(currentNode)); | ||
134 | |||
135 | ret.add(sc); | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | return new SCCResult<V>(ret, g); | ||
142 | } | ||
143 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCProperty.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCProperty.java new file mode 100644 index 00000000..51ee834e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCResult.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCResult.java new file mode 100644 index 00000000..2e511fd6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.alg.misc.scc; | ||
11 | |||
12 | import java.util.Map.Entry; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java new file mode 100644 index 00000000..89be6804 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java | |||
@@ -0,0 +1,73 @@ | |||
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.rete.itc.alg.misc.topsort; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; | ||
13 | |||
14 | import java.util.*; | ||
15 | |||
16 | /** | ||
17 | * @since 1.6 | ||
18 | */ | ||
19 | public class TopologicalSorting { | ||
20 | |||
21 | private TopologicalSorting() {/*Utility class constructor*/} | ||
22 | |||
23 | private static final class Pair<T> { | ||
24 | public T element; | ||
25 | public boolean isParent; | ||
26 | |||
27 | public Pair(final T element, final boolean isParent) { | ||
28 | this.element = element; | ||
29 | this.isParent = isParent; | ||
30 | } | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Returns a topological ordering for the given graph data source. | ||
35 | * 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. | ||
36 | * | ||
37 | * @param gds the graph data source | ||
38 | * @return a topological ordering | ||
39 | */ | ||
40 | public static <T> List<T> compute(final IGraphDataSource<T> gds) { | ||
41 | final Set<T> visited = new HashSet<T>(); | ||
42 | final LinkedList<T> result = new LinkedList<T>(); | ||
43 | final Deque<Pair<T>> dfsStack = new ArrayDeque<Pair<T>>(); | ||
44 | |||
45 | for (final T node : gds.getAllNodes()) { | ||
46 | if (!visited.contains(node)) { | ||
47 | dfsStack.addLast(new Pair<T>(node, false)); | ||
48 | } | ||
49 | |||
50 | while (!dfsStack.isEmpty()) { | ||
51 | final Pair<T> head = dfsStack.removeLast(); | ||
52 | final T source = head.element; | ||
53 | |||
54 | if (head.isParent) { | ||
55 | // we have already seen source, push it to the resulting stack | ||
56 | result.addFirst(source); | ||
57 | } else { | ||
58 | // first time we see source, continue with its children | ||
59 | visited.add(source); | ||
60 | dfsStack.addLast(new Pair<T>(source, true)); | ||
61 | |||
62 | for (final T target : gds.getTargetNodes(source).distinctValues()) { | ||
63 | if (!visited.contains(target)) { | ||
64 | dfsStack.addLast(new Pair<T>(target, false)); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | return result; | ||
72 | } | ||
73 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java new file mode 100644 index 00000000..794dabc0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java | |||
@@ -0,0 +1,174 @@ | |||
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.rete.itc.alg.representative; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; | ||
9 | import tools.refinery.viatra.runtime.rete.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(Set<Object> toMerge) { | ||
49 | if (toMerge.isEmpty()) { | ||
50 | return; | ||
51 | } | ||
52 | var representativesToMerge = new HashSet<>(); | ||
53 | Object bestRepresentative = null; | ||
54 | Set<Object> bestSet = null; | ||
55 | for (var object : toMerge) { | ||
56 | var representative = getRepresentative(object); | ||
57 | if (representativesToMerge.add(representative)) { | ||
58 | var component = getComponent(representative); | ||
59 | if (bestSet == null || bestSet.size() < component.size()) { | ||
60 | bestRepresentative = representative; | ||
61 | bestSet = component; | ||
62 | } | ||
63 | } | ||
64 | } | ||
65 | if (bestRepresentative == null) { | ||
66 | throw new AssertionError("Could not determine best representative"); | ||
67 | } | ||
68 | for (var representative : representativesToMerge) { | ||
69 | if (!bestRepresentative.equals(representative)) { | ||
70 | components.remove(representative); | ||
71 | } | ||
72 | } | ||
73 | components.put(bestRepresentative, toMerge); | ||
74 | for (var object : toMerge) { | ||
75 | var previousRepresentative = representatives.put(object, bestRepresentative); | ||
76 | if (!bestSet.contains(object)) { | ||
77 | notifyToObservers(object, previousRepresentative, bestRepresentative); | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | protected void merge(Object leftRepresentative, Object rightRepresentative) { | ||
83 | if (leftRepresentative.equals(rightRepresentative)) { | ||
84 | return; | ||
85 | } | ||
86 | var leftSet = getComponent(leftRepresentative); | ||
87 | var rightSet = getComponent(rightRepresentative); | ||
88 | if (leftSet.size() < rightSet.size()) { | ||
89 | merge(rightRepresentative, rightSet, leftRepresentative, leftSet); | ||
90 | } else { | ||
91 | merge(leftRepresentative, leftSet, rightRepresentative, rightSet); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | private void merge(Object preservedRepresentative, Set<Object> preservedSet, Object removedRepresentative, | ||
96 | Set<Object> removedSet) { | ||
97 | components.remove(removedRepresentative); | ||
98 | for (var node : removedSet) { | ||
99 | representatives.put(node, preservedRepresentative); | ||
100 | preservedSet.add(node); | ||
101 | notifyToObservers(node, removedRepresentative, preservedRepresentative); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | protected void assignNewRepresentative(Object oldRepresentative, Set<Object> set) { | ||
106 | var iterator = set.iterator(); | ||
107 | if (!iterator.hasNext()) { | ||
108 | return; | ||
109 | } | ||
110 | var newRepresentative = iterator.next(); | ||
111 | components.put(newRepresentative, set); | ||
112 | for (var node : set) { | ||
113 | var oldRepresentativeOfNode = representatives.put(node, newRepresentative); | ||
114 | if (!oldRepresentative.equals(oldRepresentativeOfNode)) { | ||
115 | throw new IllegalArgumentException("Node %s was not represented by %s but by %s" | ||
116 | .formatted(node, oldRepresentative, oldRepresentativeOfNode)); | ||
117 | } | ||
118 | notifyToObservers(node, oldRepresentative, newRepresentative); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | public void setObserver(RepresentativeObserver observer) { | ||
123 | this.observer = observer; | ||
124 | } | ||
125 | |||
126 | public Map<Object, Set<Object>> getComponents() { | ||
127 | return components; | ||
128 | } | ||
129 | |||
130 | public Object getRepresentative(Object node) { | ||
131 | return representatives.get(node); | ||
132 | } | ||
133 | |||
134 | public Set<Object> getComponent(Object representative) { | ||
135 | return components.get(representative); | ||
136 | } | ||
137 | |||
138 | public void dispose() { | ||
139 | graph.detachObserver(this); | ||
140 | } | ||
141 | |||
142 | @Override | ||
143 | public void nodeInserted(Object n) { | ||
144 | var component = new HashSet<>(1); | ||
145 | component.add(n); | ||
146 | initializeSet(component); | ||
147 | notifyToObservers(n, n, Direction.INSERT); | ||
148 | } | ||
149 | |||
150 | @Override | ||
151 | public void nodeDeleted(Object n) { | ||
152 | var representative = representatives.remove(n); | ||
153 | if (!representative.equals(n)) { | ||
154 | throw new IllegalStateException("Trying to delete node with dangling edges"); | ||
155 | } | ||
156 | components.remove(representative); | ||
157 | notifyToObservers(n, representative, Direction.DELETE); | ||
158 | } | ||
159 | |||
160 | protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) { | ||
161 | notifyToObservers(node, oldRepresentative, Direction.DELETE); | ||
162 | notifyToObservers(node, newRepresentative, Direction.INSERT); | ||
163 | } | ||
164 | |||
165 | protected void notifyToObservers(Object node, Object representative, Direction direction) { | ||
166 | if (observer != null) { | ||
167 | observer.tupleChanged(node, representative, direction); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | public interface Factory { | ||
172 | RepresentativeElectionAlgorithm create(Graph<Object> graph); | ||
173 | } | ||
174 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeObserver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeObserver.java new file mode 100644 index 00000000..6b772fa8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..0463301b --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java | |||
@@ -0,0 +1,69 @@ | |||
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.rete.itc.alg.representative; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.GraphHelper; | ||
9 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs.BFS; | ||
10 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC; | ||
11 | import tools.refinery.viatra.runtime.rete.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 | var sources = BFS.reachableSources(graph, target); | ||
39 | var targets = BFS.reachableTargets(graph, source); | ||
40 | sources.retainAll(targets); | ||
41 | merge(sources); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public void edgeDeleted(Object source, Object target) { | ||
47 | var sourceRoot = getRepresentative(source); | ||
48 | var targetRoot = getRepresentative(target); | ||
49 | if (!sourceRoot.equals(targetRoot)) { | ||
50 | // New edge does not change strongly connected components. | ||
51 | return; | ||
52 | } | ||
53 | var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph); | ||
54 | if (!BFS.isReachable(source, target, component)) { | ||
55 | var newSCCs = SCC.computeSCC(component).getSccs(); | ||
56 | split(sourceRoot, newSCCs); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | private void split(Object preservedRepresentative, Collection<? extends Set<Object>> sets) { | ||
61 | for (var set : sets) { | ||
62 | if (set.contains(preservedRepresentative)) { | ||
63 | components.put(preservedRepresentative, set); | ||
64 | } else { | ||
65 | assignNewRepresentative(preservedRepresentative, set); | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..704f0235 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.alg.representative; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/util/CollectionHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/util/CollectionHelper.java new file mode 100644 index 00000000..6655be6d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.alg.util; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
12 | |||
13 | import java.util.Set; | ||
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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java new file mode 100644 index 00000000..f7f6b5ed --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.graphimpl; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC; | ||
12 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/Graph.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/Graph.java new file mode 100644 index 00000000..91604cb2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.graphimpl; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; | ||
13 | import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver; | ||
14 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalGraphDataSource.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalGraphDataSource.java new file mode 100644 index 00000000..4fcaa71f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalWrapper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalWrapper.java new file mode 100644 index 00000000..c4315ca2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphDataSource.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphDataSource.java new file mode 100644 index 00000000..9159a692 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphObserver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphObserver.java new file mode 100644 index 00000000..a282216d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcDataSource.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcDataSource.java new file mode 100644 index 00000000..5ede600f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.itc.igraph; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcObserver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcObserver.java new file mode 100644 index 00000000..74e0cb75 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/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.rete.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-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java new file mode 100644 index 00000000..83e86fe6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java | |||
@@ -0,0 +1,49 @@ | |||
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.rete.matcher; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
13 | |||
14 | /** | ||
15 | * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use delete and re-derive | ||
16 | * evaluation. | ||
17 | * | ||
18 | * @author Tamas Szabo | ||
19 | * @since 2.2 | ||
20 | */ | ||
21 | public class DRedReteBackendFactory extends ReteBackendFactory { | ||
22 | |||
23 | public static final DRedReteBackendFactory INSTANCE = new DRedReteBackendFactory(); | ||
24 | |||
25 | @Override | ||
26 | public IQueryBackend create(IQueryBackendContext context) { | ||
27 | return create(context, true, null); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public int hashCode() { | ||
32 | return DRedReteBackendFactory.class.hashCode(); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public boolean equals(final Object obj) { | ||
37 | if (this == obj) { | ||
38 | return true; | ||
39 | } | ||
40 | if (obj == null) { | ||
41 | return false; | ||
42 | } | ||
43 | if (!(obj instanceof DRedReteBackendFactory)) { | ||
44 | return false; | ||
45 | } | ||
46 | return true; | ||
47 | } | ||
48 | |||
49 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java new file mode 100644 index 00000000..a4fa4914 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java | |||
@@ -0,0 +1,46 @@ | |||
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.rete.matcher; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
16 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * A configurable hint provider that gathers hints for queries during runtime, and delegates defaults to an external hint provider. | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | * @since 1.5 | ||
23 | */ | ||
24 | class HintConfigurator implements IQueryBackendHintProvider { | ||
25 | |||
26 | private IQueryBackendHintProvider defaultHintProvider; | ||
27 | private Map<PQuery, QueryEvaluationHint> storedHints = new HashMap<PQuery, QueryEvaluationHint>(); | ||
28 | |||
29 | public HintConfigurator(IQueryBackendHintProvider defaultHintProvider) { | ||
30 | this.defaultHintProvider = defaultHintProvider; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public QueryEvaluationHint getQueryEvaluationHint(PQuery query) { | ||
35 | return defaultHintProvider.getQueryEvaluationHint(query).overrideBy(storedHints.get(query)); | ||
36 | } | ||
37 | |||
38 | public void storeHint(PQuery query, QueryEvaluationHint hint) { | ||
39 | QueryEvaluationHint oldHint = storedHints.get(query); | ||
40 | if (oldHint == null) | ||
41 | storedHints.put(query, hint); | ||
42 | else | ||
43 | storedHints.put(query, oldHint.overrideBy(hint)); | ||
44 | } | ||
45 | |||
46 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java new file mode 100644 index 00000000..4c64a1a1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java | |||
@@ -0,0 +1,30 @@ | |||
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.rete.matcher; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
12 | |||
13 | /** | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public class IncrementalMatcherCapability implements IMatcherCapability { | ||
19 | |||
20 | @Override | ||
21 | public boolean canBeSubstitute(IMatcherCapability capability) { | ||
22 | /* | ||
23 | * TODO: for now, as we are only prepared for Rete and LS, we can assume that | ||
24 | * a matcher created with Rete can always be a substitute for a matcher created | ||
25 | * by any backend. | ||
26 | */ | ||
27 | return true; | ||
28 | } | ||
29 | |||
30 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java new file mode 100644 index 00000000..347cabc4 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java | |||
@@ -0,0 +1,100 @@ | |||
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.rete.matcher; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
14 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
17 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
18 | import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler; | ||
19 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
20 | |||
21 | public class ReteBackendFactory implements IQueryBackendFactory { | ||
22 | /** | ||
23 | * EXPERIMENTAL | ||
24 | */ | ||
25 | protected static final int reteThreads = 0; | ||
26 | |||
27 | /** | ||
28 | * @since 2.0 | ||
29 | */ | ||
30 | public static final ReteBackendFactory INSTANCE = new ReteBackendFactory(); | ||
31 | |||
32 | /** | ||
33 | * @deprecated Use the static {@link #INSTANCE} field instead | ||
34 | */ | ||
35 | @Deprecated | ||
36 | public ReteBackendFactory() { | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * @since 1.5 | ||
41 | */ | ||
42 | @Override | ||
43 | public IQueryBackend create(IQueryBackendContext context) { | ||
44 | return create(context, false, null); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @since 2.4 | ||
49 | */ | ||
50 | public IQueryBackend create(IQueryBackendContext context, boolean deleteAndRederiveEvaluation, | ||
51 | TimelyConfiguration timelyConfiguration) { | ||
52 | ReteEngine engine; | ||
53 | engine = new ReteEngine(context, reteThreads, deleteAndRederiveEvaluation, timelyConfiguration); | ||
54 | IQueryBackendHintProvider hintConfiguration = engine.getHintConfiguration(); | ||
55 | ReteRecipeCompiler compiler = new ReteRecipeCompiler( | ||
56 | Options.builderMethod.layoutStrategy(context, hintConfiguration), context.getLogger(), | ||
57 | context.getRuntimeContext().getMetaContext(), context.getQueryCacheContext(), hintConfiguration, | ||
58 | context.getQueryAnalyzer(), deleteAndRederiveEvaluation, timelyConfiguration); | ||
59 | engine.setCompiler(compiler); | ||
60 | return engine; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public Class<? extends IQueryBackend> getBackendClass() { | ||
65 | return ReteEngine.class; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public int hashCode() { | ||
70 | return ReteBackendFactory.class.hashCode(); | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public boolean equals(Object obj) { | ||
75 | if (this == obj) { | ||
76 | return true; | ||
77 | } | ||
78 | if (obj == null) { | ||
79 | return false; | ||
80 | } | ||
81 | if (!(obj instanceof ReteBackendFactory)) { | ||
82 | return false; | ||
83 | } | ||
84 | return true; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * @since 1.4 | ||
89 | */ | ||
90 | @Override | ||
91 | public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) { | ||
92 | return new IncrementalMatcherCapability(); | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public boolean isCaching() { | ||
97 | return true; | ||
98 | } | ||
99 | |||
100 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java new file mode 100644 index 00000000..98775ab3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java | |||
@@ -0,0 +1,35 @@ | |||
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.rete.matcher; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider; | ||
13 | |||
14 | /** | ||
15 | * @since 2.0 | ||
16 | * | ||
17 | */ | ||
18 | public class ReteBackendFactoryProvider implements IQueryBackendFactoryProvider { | ||
19 | |||
20 | @Override | ||
21 | public IQueryBackendFactory getFactory() { | ||
22 | return ReteBackendFactory.INSTANCE; | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | public boolean isSystemDefaultEngine() { | ||
27 | return true; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public boolean isSystemDefaultCachingBackend() { | ||
32 | return true; | ||
33 | } | ||
34 | |||
35 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java new file mode 100644 index 00000000..9bd499f4 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java | |||
@@ -0,0 +1,579 @@ | |||
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.rete.matcher; | ||
11 | |||
12 | import java.lang.reflect.InvocationTargetException; | ||
13 | import java.util.Collection; | ||
14 | import java.util.LinkedList; | ||
15 | import java.util.Map; | ||
16 | import java.util.concurrent.Callable; | ||
17 | |||
18 | import org.apache.log4j.Logger; | ||
19 | import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; | ||
20 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
21 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; | ||
22 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
23 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
24 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
25 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
26 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
27 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
28 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
29 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
30 | import tools.refinery.viatra.runtime.rete.boundary.Disconnectable; | ||
31 | import tools.refinery.viatra.runtime.rete.boundary.ReteBoundary; | ||
32 | import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException; | ||
33 | import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler; | ||
34 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
35 | import tools.refinery.viatra.runtime.rete.network.Network; | ||
36 | import tools.refinery.viatra.runtime.rete.network.NodeProvisioner; | ||
37 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
38 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
39 | |||
40 | /** | ||
41 | * @author Gabor Bergmann | ||
42 | * | ||
43 | */ | ||
44 | public class ReteEngine implements IQueryBackend { | ||
45 | |||
46 | protected Network reteNet; | ||
47 | protected final int reteThreads; | ||
48 | protected ReteBoundary boundary; | ||
49 | |||
50 | /** | ||
51 | * @since 2.2 | ||
52 | */ | ||
53 | protected final boolean deleteAndRederiveEvaluation; | ||
54 | /** | ||
55 | * @since 2.4 | ||
56 | */ | ||
57 | protected final TimelyConfiguration timelyConfiguration; | ||
58 | |||
59 | private IQueryBackendContext context; | ||
60 | private Logger logger; | ||
61 | protected IQueryRuntimeContext runtimeContext; | ||
62 | |||
63 | protected Collection<Disconnectable> disconnectables; | ||
64 | |||
65 | protected Map<PQuery, RetePatternMatcher> matchers; | ||
66 | |||
67 | protected ReteRecipeCompiler compiler; | ||
68 | |||
69 | protected final boolean parallelExecutionEnabled; // TRUE if model manipulation can go on | ||
70 | |||
71 | private boolean disposedOrUninitialized = true; | ||
72 | |||
73 | private HintConfigurator hintConfigurator; | ||
74 | |||
75 | /** | ||
76 | * @param context | ||
77 | * the context of the pattern matcher, conveying all information from the outside world. | ||
78 | * @param reteThreads | ||
79 | * the number of threads to operate the RETE network with; 0 means single-threaded operation, 1 starts an | ||
80 | * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers. | ||
81 | */ | ||
82 | public ReteEngine(IQueryBackendContext context, int reteThreads) { | ||
83 | this(context, reteThreads, false, null); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * @since 2.4 | ||
88 | */ | ||
89 | public ReteEngine(IQueryBackendContext context, int reteThreads, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyConfiguration) { | ||
90 | super(); | ||
91 | this.context = context; | ||
92 | this.logger = context.getLogger(); | ||
93 | this.runtimeContext = context.getRuntimeContext(); | ||
94 | this.reteThreads = reteThreads; | ||
95 | this.parallelExecutionEnabled = reteThreads > 0; | ||
96 | this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation; | ||
97 | this.timelyConfiguration = timelyConfiguration; | ||
98 | initEngine(); | ||
99 | this.compiler = null; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * @since 1.6 | ||
104 | */ | ||
105 | public IQueryBackendContext getBackendContext() { | ||
106 | return context; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * @since 2.2 | ||
111 | */ | ||
112 | public boolean isDeleteAndRederiveEvaluation() { | ||
113 | return this.deleteAndRederiveEvaluation; | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * @since 2.4 | ||
118 | */ | ||
119 | public TimelyConfiguration getTimelyConfiguration() { | ||
120 | return this.timelyConfiguration; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * initializes engine components | ||
125 | */ | ||
126 | private synchronized void initEngine() { | ||
127 | this.disposedOrUninitialized = false; | ||
128 | this.disconnectables = new LinkedList<Disconnectable>(); | ||
129 | // this.caughtExceptions = new LinkedBlockingQueue<Throwable>(); | ||
130 | |||
131 | |||
132 | this.hintConfigurator = new HintConfigurator(context.getHintProvider()); | ||
133 | |||
134 | this.reteNet = new Network(reteThreads, this); | ||
135 | this.boundary = new ReteBoundary(this); // prerequisite: network | ||
136 | |||
137 | this.matchers = CollectionsFactory.createMap(); | ||
138 | /* this.matchersScoped = new HashMap<PatternDescription, Map<Map<Integer,Scope>,RetePatternMatcher>>(); */ | ||
139 | |||
140 | // prerequisite: network, framework, boundary, disconnectables | ||
141 | //context.subscribeBackendForUpdates(this.boundary); | ||
142 | // prerequisite: boundary, disconnectables | ||
143 | // this.traceListener = context.subscribePatternMatcherForTraceInfluences(this); | ||
144 | |||
145 | } | ||
146 | |||
147 | @Override | ||
148 | public void flushUpdates() { | ||
149 | for (ReteContainer container : this.reteNet.getContainers()) { | ||
150 | container.deliverMessagesSingleThreaded(); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * deconstructs engine components | ||
156 | */ | ||
157 | private synchronized void deconstructEngine() { | ||
158 | ensureInitialized(); | ||
159 | reteNet.kill(); | ||
160 | |||
161 | //context.unSubscribeBackendFromUpdates(this.boundary); | ||
162 | for (Disconnectable disc : disconnectables) { | ||
163 | disc.disconnect(); | ||
164 | } | ||
165 | |||
166 | this.matchers = null; | ||
167 | this.disconnectables = null; | ||
168 | |||
169 | this.reteNet = null; | ||
170 | this.boundary = null; | ||
171 | |||
172 | this.hintConfigurator = null; | ||
173 | |||
174 | // this.machineListener = new MachineListener(this); // prerequisite: | ||
175 | // framework, disconnectables | ||
176 | // this.traceListener = null; | ||
177 | |||
178 | this.disposedOrUninitialized = true; | ||
179 | } | ||
180 | |||
181 | /** | ||
182 | * Deconstructs the engine to get rid of it finally | ||
183 | */ | ||
184 | public void killEngine() { | ||
185 | deconstructEngine(); | ||
186 | // this.framework = null; | ||
187 | this.compiler = null; | ||
188 | this.logger = null; | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * Resets the engine to an after-initialization phase | ||
193 | * | ||
194 | */ | ||
195 | public void reset() { | ||
196 | deconstructEngine(); | ||
197 | |||
198 | initEngine(); | ||
199 | |||
200 | compiler.reset(); | ||
201 | } | ||
202 | |||
203 | /** | ||
204 | * Accesses the patternmatcher for a given pattern, constructs one if a matcher is not available yet. | ||
205 | * | ||
206 | * @pre: builder is set. | ||
207 | * @param query | ||
208 | * the pattern to be matched. | ||
209 | * @return a patternmatcher object that can match occurences of the given pattern. | ||
210 | * @throws ViatraQueryRuntimeException | ||
211 | * if construction fails. | ||
212 | */ | ||
213 | public synchronized RetePatternMatcher accessMatcher(final PQuery query) { | ||
214 | ensureInitialized(); | ||
215 | RetePatternMatcher matcher; | ||
216 | // String namespace = gtPattern.getNamespace().getName(); | ||
217 | // String name = gtPattern.getName(); | ||
218 | // String fqn = namespace + "." + name; | ||
219 | matcher = matchers.get(query); | ||
220 | if (matcher == null) { | ||
221 | constructionWrapper(() -> { | ||
222 | RecipeTraceInfo prodNode; | ||
223 | prodNode = boundary.accessProductionTrace(query); | ||
224 | |||
225 | RetePatternMatcher retePatternMatcher = new RetePatternMatcher(ReteEngine.this, | ||
226 | prodNode); | ||
227 | retePatternMatcher.setTag(query); | ||
228 | matchers.put(query, retePatternMatcher); | ||
229 | return null; | ||
230 | }); | ||
231 | matcher = matchers.get(query); | ||
232 | } | ||
233 | |||
234 | executeDelayedCommands(); | ||
235 | |||
236 | return matcher; | ||
237 | } | ||
238 | |||
239 | |||
240 | /** | ||
241 | * Constructs RETE pattern matchers for a collection of patterns, if they are not available yet. Model traversal | ||
242 | * during the whole construction period is coalesced (which may have an effect on performance, depending on the | ||
243 | * matcher context). | ||
244 | * | ||
245 | * @pre: builder is set. | ||
246 | * @param specifications | ||
247 | * the patterns to be matched. | ||
248 | * @throws ViatraQueryRuntimeException | ||
249 | * if construction fails. | ||
250 | */ | ||
251 | public synchronized void buildMatchersCoalesced(final Collection<PQuery> specifications) { | ||
252 | ensureInitialized(); | ||
253 | constructionWrapper(() -> { | ||
254 | for (PQuery specification : specifications) { | ||
255 | boundary.accessProductionNode(specification); | ||
256 | } | ||
257 | return null; | ||
258 | }); | ||
259 | } | ||
260 | |||
261 | /** | ||
262 | * @since 2.4 | ||
263 | */ | ||
264 | public <T> T constructionWrapper(final Callable<T> payload) { | ||
265 | T result = null; | ||
266 | // context.modelReadLock(); | ||
267 | // try { | ||
268 | if (parallelExecutionEnabled) | ||
269 | reteNet.getStructuralChangeLock().lock(); | ||
270 | try { | ||
271 | try { | ||
272 | result = runtimeContext.coalesceTraversals(() -> { | ||
273 | T innerResult = payload.call(); | ||
274 | this.executeDelayedCommands(); | ||
275 | return innerResult; | ||
276 | }); | ||
277 | } catch (InvocationTargetException ex) { | ||
278 | final Throwable cause = ex.getCause(); | ||
279 | if (cause instanceof RetePatternBuildException) | ||
280 | throw (RetePatternBuildException) cause; | ||
281 | if (cause instanceof RuntimeException) | ||
282 | throw (RuntimeException) cause; | ||
283 | assert (false); | ||
284 | } | ||
285 | } finally { | ||
286 | if (parallelExecutionEnabled) | ||
287 | reteNet.getStructuralChangeLock().unlock(); | ||
288 | reteNet.waitForReteTermination(); | ||
289 | } | ||
290 | // } finally { | ||
291 | // context.modelReadUnLock(); | ||
292 | // } | ||
293 | return result; | ||
294 | } | ||
295 | |||
296 | // /** | ||
297 | // * Accesses the patternmatcher for a given pattern with additional scoping, constructs one if | ||
298 | // * a matcher is not available yet. | ||
299 | // * | ||
300 | // * @param gtPattern | ||
301 | // * the pattern to be matched. | ||
302 | // * @param additionalScopeMap | ||
303 | // * additional, optional scopes for the symbolic parameters | ||
304 | // * maps the position of the symbolic parameter to its additional scope (if any) | ||
305 | // * @pre: scope.parent is non-root, i.e. this is a nontrivial constraint | ||
306 | // * use the static method RetePatternMatcher.buildAdditionalScopeMap() to create from PatternCallSignature | ||
307 | // * @return a patternmatcher object that can match occurences of the given | ||
308 | // * pattern. | ||
309 | // * @throws PatternMatcherCompileTimeException | ||
310 | // * if construction fails. | ||
311 | // */ | ||
312 | // public synchronized RetePatternMatcher accessMatcherScoped(PatternDescription gtPattern, Map<Integer, Scope> | ||
313 | // additionalScopeMap) | ||
314 | // throws PatternMatcherCompileTimeException { | ||
315 | // if (additionalScopeMap.isEmpty()) return accessMatcher(gtPattern); | ||
316 | // | ||
317 | // RetePatternMatcher matcher; | ||
318 | // | ||
319 | // Map<Map<Integer, Scope>, RetePatternMatcher> scopes = matchersScoped.get(gtPattern); | ||
320 | // if (scopes == null) { | ||
321 | // scopes = new HashMap<Map<Integer, Scope>, RetePatternMatcher>(); | ||
322 | // matchersScoped.put(gtPattern, scopes); | ||
323 | // } | ||
324 | // | ||
325 | // matcher = scopes.get(additionalScopeMap); | ||
326 | // if (matcher == null) { | ||
327 | // context.modelReadLock(); | ||
328 | // try { | ||
329 | // reteNet.getStructuralChangeLock().lock(); | ||
330 | // try { | ||
331 | // Address<? extends Production> prodNode; | ||
332 | // prodNode = boundary.accessProductionScoped(gtPattern, additionalScopeMap); | ||
333 | // | ||
334 | // matcher = new RetePatternMatcher(this, prodNode); | ||
335 | // scopes.put(additionalScopeMap, matcher); | ||
336 | // } finally { | ||
337 | // reteNet.getStructuralChangeLock().unlock(); | ||
338 | // } | ||
339 | // } finally { | ||
340 | // context.modelReadUnLock(); | ||
341 | // } | ||
342 | // // reteNet.flushUpdates(); | ||
343 | // } | ||
344 | // | ||
345 | // return matcher; | ||
346 | // } | ||
347 | |||
348 | /** | ||
349 | * Returns an indexer that groups the contents of this Production node by their projections to a given mask. | ||
350 | * Designed to be called by a RetePatternMatcher. | ||
351 | * | ||
352 | * @param production | ||
353 | * the production node to be indexed. | ||
354 | * @param mask | ||
355 | * the mask that defines the projection. | ||
356 | * @return the Indexer. | ||
357 | */ | ||
358 | synchronized Indexer accessProjection(RecipeTraceInfo production, TupleMask mask) { | ||
359 | ensureInitialized(); | ||
360 | NodeProvisioner nodeProvisioner = reteNet.getHeadContainer().getProvisioner(); | ||
361 | Indexer result = nodeProvisioner.peekProjectionIndexer(production, mask); | ||
362 | if (result == null) { | ||
363 | result = constructionWrapper(() -> | ||
364 | nodeProvisioner.accessProjectionIndexerOnetime(production, mask) | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | return result; | ||
369 | } | ||
370 | |||
371 | // /** | ||
372 | // * Retrieves the patternmatcher for a given pattern fqn, returns null if | ||
373 | // the matching network hasn't been constructed yet. | ||
374 | // * | ||
375 | // * @param fqn the fully qualified name of the pattern to be matched. | ||
376 | // * @return the previously constructed patternmatcher object that can match | ||
377 | // occurences of the given pattern, or null if it doesn't exist. | ||
378 | // */ | ||
379 | // public RetePatternMatcher getMatcher(String fqn) | ||
380 | // { | ||
381 | // RetePatternMatcher matcher = matchersByFqn.get(fqn); | ||
382 | // if (matcher == null) | ||
383 | // { | ||
384 | // Production prodNode = boundary.getProduction(fqn); | ||
385 | // | ||
386 | // matcher = new RetePatternMatcher(this, prodNode); | ||
387 | // matchersByFqn.put(fqn, matcher); | ||
388 | // } | ||
389 | // | ||
390 | // return matcher; | ||
391 | // } | ||
392 | |||
393 | /** | ||
394 | * @since 2.3 | ||
395 | */ | ||
396 | public void executeDelayedCommands() { | ||
397 | for (final ReteContainer container : this.reteNet.getContainers()) { | ||
398 | container.executeDelayedCommands(); | ||
399 | } | ||
400 | } | ||
401 | |||
402 | /** | ||
403 | * Waits until the pattern matcher is in a steady state and output can be retrieved. | ||
404 | */ | ||
405 | public void settle() { | ||
406 | ensureInitialized(); | ||
407 | reteNet.waitForReteTermination(); | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * Waits until the pattern matcher is in a steady state and output can be retrieved. When steady state is reached, a | ||
412 | * retrieval action is executed before the steady state ceases. | ||
413 | * | ||
414 | * @param action | ||
415 | * the action to be run when reaching the steady-state. | ||
416 | */ | ||
417 | public void settle(Runnable action) { | ||
418 | ensureInitialized(); | ||
419 | reteNet.waitForReteTermination(action); | ||
420 | } | ||
421 | |||
422 | // /** | ||
423 | // * @return the framework | ||
424 | // */ | ||
425 | // public IFramework getFramework() { | ||
426 | // return framework.get(); | ||
427 | // } | ||
428 | |||
429 | /** | ||
430 | * @return the reteNet | ||
431 | */ | ||
432 | public Network getReteNet() { | ||
433 | ensureInitialized(); | ||
434 | return reteNet; | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * @return the boundary | ||
439 | */ | ||
440 | public ReteBoundary getBoundary() { | ||
441 | ensureInitialized(); | ||
442 | return boundary; | ||
443 | } | ||
444 | |||
445 | // /** | ||
446 | // * @return the pattern matcher builder | ||
447 | // */ | ||
448 | // public IRetePatternBuilder getBuilder() { | ||
449 | // return builder; | ||
450 | // } | ||
451 | |||
452 | /** | ||
453 | * @param builder | ||
454 | * the pattern matcher builder to set | ||
455 | */ | ||
456 | public void setCompiler(ReteRecipeCompiler builder) { | ||
457 | ensureInitialized(); | ||
458 | this.compiler = builder; | ||
459 | } | ||
460 | |||
461 | // /** | ||
462 | // * @return the manipulationListener | ||
463 | // */ | ||
464 | // public IManipulationListener getManipulationListener() { | ||
465 | // ensureInitialized(); | ||
466 | // return manipulationListener; | ||
467 | // } | ||
468 | |||
469 | // /** | ||
470 | // * @return the traceListener | ||
471 | // */ | ||
472 | // public IPredicateTraceListener geTraceListener() { | ||
473 | // ensureInitialized(); | ||
474 | // return traceListener; | ||
475 | // } | ||
476 | |||
477 | /** | ||
478 | * @param disc | ||
479 | * the new Disconnectable adapter. | ||
480 | */ | ||
481 | public void addDisconnectable(Disconnectable disc) { | ||
482 | ensureInitialized(); | ||
483 | disconnectables.add(disc); | ||
484 | } | ||
485 | |||
486 | /** | ||
487 | * @return the parallelExecutionEnabled | ||
488 | */ | ||
489 | public boolean isParallelExecutionEnabled() { | ||
490 | return parallelExecutionEnabled; | ||
491 | } | ||
492 | |||
493 | |||
494 | public Logger getLogger() { | ||
495 | ensureInitialized(); | ||
496 | return logger; | ||
497 | } | ||
498 | |||
499 | public IQueryRuntimeContext getRuntimeContext() { | ||
500 | ensureInitialized(); | ||
501 | return runtimeContext; | ||
502 | } | ||
503 | |||
504 | public ReteRecipeCompiler getCompiler() { | ||
505 | ensureInitialized(); | ||
506 | return compiler; | ||
507 | } | ||
508 | |||
509 | // /** | ||
510 | // * For internal use only: logs exceptions occurring during term evaluation inside the RETE net. | ||
511 | // * @param e | ||
512 | // */ | ||
513 | // public void logEvaluatorException(Throwable e) { | ||
514 | // try { | ||
515 | // caughtExceptions.put(e); | ||
516 | // } catch (InterruptedException e1) { | ||
517 | // logEvaluatorException(e); | ||
518 | // } | ||
519 | // } | ||
520 | // /** | ||
521 | // * Polls the exceptions caught and logged during term evaluation by this RETE engine. | ||
522 | // * Recommended usage: iterate polling until null is returned. | ||
523 | // * | ||
524 | // * @return the next caught exception, or null if there are no more. | ||
525 | // */ | ||
526 | // public Throwable getNextLoggedEvaluatorException() { | ||
527 | // return caughtExceptions.poll(); | ||
528 | // } | ||
529 | |||
530 | void ensureInitialized() { | ||
531 | if (disposedOrUninitialized) | ||
532 | throw new IllegalStateException("Trying to use a Rete engine that has been disposed or has not yet been initialized."); | ||
533 | |||
534 | } | ||
535 | |||
536 | @Override | ||
537 | public IQueryResultProvider getResultProvider(PQuery query) { | ||
538 | return accessMatcher(query); | ||
539 | } | ||
540 | |||
541 | /** | ||
542 | * @since 1.4 | ||
543 | */ | ||
544 | @Override | ||
545 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints) { | ||
546 | hintConfigurator.storeHint(query, hints); | ||
547 | return accessMatcher(query); | ||
548 | } | ||
549 | |||
550 | @Override | ||
551 | public IQueryResultProvider peekExistingResultProvider(PQuery query) { | ||
552 | ensureInitialized(); | ||
553 | return matchers.get(query); | ||
554 | } | ||
555 | |||
556 | @Override | ||
557 | public void dispose() { | ||
558 | killEngine(); | ||
559 | } | ||
560 | |||
561 | @Override | ||
562 | public boolean isCaching() { | ||
563 | return true; | ||
564 | } | ||
565 | |||
566 | /** | ||
567 | * @since 1.5 | ||
568 | * @noreference Internal API, subject to change | ||
569 | */ | ||
570 | public IQueryBackendHintProvider getHintConfiguration() { | ||
571 | return hintConfigurator; | ||
572 | } | ||
573 | |||
574 | @Override | ||
575 | public IQueryBackendFactory getFactory() { | ||
576 | return ReteBackendFactory.INSTANCE; | ||
577 | } | ||
578 | |||
579 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java new file mode 100644 index 00000000..38fe7c2f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java | |||
@@ -0,0 +1,463 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | |||
11 | package tools.refinery.viatra.runtime.rete.matcher; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
14 | import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; | ||
15 | import tools.refinery.viatra.runtime.matchers.backend.IUpdateable; | ||
16 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
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.Accuracy; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
24 | import tools.refinery.viatra.runtime.rete.index.IterableIndexer; | ||
25 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
26 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
27 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
28 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
29 | import tools.refinery.viatra.runtime.rete.single.CallbackNode; | ||
30 | import tools.refinery.viatra.runtime.rete.single.TransformerNode; | ||
31 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
32 | |||
33 | import java.util.Collection; | ||
34 | import java.util.List; | ||
35 | import java.util.Map; | ||
36 | import java.util.Optional; | ||
37 | import java.util.stream.Collectors; | ||
38 | import java.util.stream.Stream; | ||
39 | |||
40 | /** | ||
41 | * @author Gabor Bergmann | ||
42 | * | ||
43 | */ | ||
44 | public class RetePatternMatcher extends TransformerNode implements IQueryResultProvider { | ||
45 | |||
46 | protected ReteEngine engine; | ||
47 | protected IQueryRuntimeContext context; | ||
48 | protected ProductionNode productionNode; | ||
49 | protected RecipeTraceInfo productionNodeTrace; | ||
50 | protected Map<String, Integer> posMapping; | ||
51 | protected Map<Object, Receiver> taggedChildren = CollectionsFactory.createMap(); | ||
52 | protected boolean connected = false; // is rete-wise connected to the | ||
53 | // production node? | ||
54 | |||
55 | /** | ||
56 | * @param productionNode | ||
57 | * a production node that matches this pattern without any parameter bindings | ||
58 | * @pre: Production must be local to the head container | ||
59 | */ | ||
60 | public RetePatternMatcher(ReteEngine engine, RecipeTraceInfo productionNodeTrace) { | ||
61 | super(engine.getReteNet().getHeadContainer()); | ||
62 | this.engine = engine; | ||
63 | this.context = engine.getRuntimeContext(); | ||
64 | this.productionNodeTrace = productionNodeTrace; | ||
65 | final Address<? extends Node> productionAddress = reteContainer.getProvisioner() | ||
66 | .getOrCreateNodeByRecipe(productionNodeTrace); | ||
67 | if (!reteContainer.isLocal(productionAddress)) | ||
68 | throw new IllegalArgumentException("@pre: Production must be local to the head container"); | ||
69 | this.productionNode = (ProductionNode) reteContainer.resolveLocal(productionAddress); | ||
70 | this.posMapping = this.productionNode.getPosMapping(); | ||
71 | this.reteContainer.getCommunicationTracker().registerDependency(this.productionNode, this); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * @since 1.6 | ||
76 | */ | ||
77 | public ProductionNode getProductionNode() { | ||
78 | return productionNode; | ||
79 | } | ||
80 | |||
81 | public Tuple matchOneRandomly(Object[] inputMapping, boolean[] fixed) { | ||
82 | List<Tuple> allMatches = matchAll(inputMapping, fixed).collect(Collectors.toList()); | ||
83 | if (allMatches == null || allMatches.isEmpty()) | ||
84 | return null; | ||
85 | else | ||
86 | return allMatches.get((int) (Math.random() * allMatches.size())); | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * @since 2.0 | ||
91 | */ | ||
92 | public Stream<Tuple> matchAll(Object[] inputMapping, boolean[] fixed) { | ||
93 | // retrieving the projection | ||
94 | TupleMask mask = TupleMask.fromKeepIndicators(fixed); | ||
95 | Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping)); | ||
96 | |||
97 | return matchAll(mask, inputSignature); | ||
98 | |||
99 | } | ||
100 | |||
101 | /** | ||
102 | * @since 2.0 | ||
103 | */ | ||
104 | public Stream<Tuple> matchAll(TupleMask mask, ITuple inputSignature) { | ||
105 | AllMatchFetcher fetcher = new AllMatchFetcher(engine.accessProjection(productionNodeTrace, mask), | ||
106 | context.wrapTuple(inputSignature.toImmutable())); | ||
107 | engine.reteNet.waitForReteTermination(fetcher); | ||
108 | return fetcher.getMatches(); | ||
109 | |||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @since 2.0 | ||
114 | */ | ||
115 | public Optional<Tuple> matchOne(Object[] inputMapping, boolean[] fixed) { | ||
116 | // retrieving the projection | ||
117 | TupleMask mask = TupleMask.fromKeepIndicators(fixed); | ||
118 | Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping)); | ||
119 | |||
120 | return matchOne(mask, inputSignature); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * @since 2.0 | ||
125 | */ | ||
126 | public Optional<Tuple> matchOne(TupleMask mask, ITuple inputSignature) { | ||
127 | SingleMatchFetcher fetcher = new SingleMatchFetcher(engine.accessProjection(productionNodeTrace, mask), | ||
128 | context.wrapTuple(inputSignature.toImmutable())); | ||
129 | engine.reteNet.waitForReteTermination(fetcher); | ||
130 | return Optional.ofNullable(fetcher.getMatch()); | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true. | ||
135 | * | ||
136 | * @return the number of occurrences | ||
137 | */ | ||
138 | public int count(Object[] inputMapping, boolean[] fixed) { | ||
139 | TupleMask mask = TupleMask.fromKeepIndicators(fixed); | ||
140 | Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping)); | ||
141 | |||
142 | return count(mask, inputSignature); | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true. | ||
147 | * | ||
148 | * @return the number of occurrences | ||
149 | * @since 1.7 | ||
150 | */ | ||
151 | public int count(TupleMask mask, ITuple inputSignature) { | ||
152 | CountFetcher fetcher = new CountFetcher(engine.accessProjection(productionNodeTrace, mask), | ||
153 | context.wrapTuple(inputSignature.toImmutable())); | ||
154 | engine.reteNet.waitForReteTermination(fetcher); | ||
155 | |||
156 | return fetcher.getCount(); | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * Counts the number of distinct tuples attainable from the match set by projecting match tuples according to the given mask. | ||
161 | * | ||
162 | * | ||
163 | * @return the size of the projection | ||
164 | * @since 2.1 | ||
165 | */ | ||
166 | public int projectionSize(TupleMask groupMask) { | ||
167 | ProjectionSizeFetcher fetcher = new ProjectionSizeFetcher( | ||
168 | (IterableIndexer) engine.accessProjection(productionNodeTrace, groupMask)); | ||
169 | engine.reteNet.waitForReteTermination(fetcher); | ||
170 | |||
171 | return fetcher.getSize(); | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * Connects a new external receiver that will receive update notifications from now on. The receiver will | ||
176 | * practically connect to the production node, the added value is unwrapping the updates for external use. | ||
177 | * | ||
178 | * @param synchronize | ||
179 | * if true, the contents of the production node will be inserted into the receiver after the connection | ||
180 | * is established. | ||
181 | */ | ||
182 | public synchronized void connect(Receiver receiver, boolean synchronize) { | ||
183 | if (!connected) { // connect to the production node as a RETE-child | ||
184 | reteContainer.connect(productionNode, this); | ||
185 | connected = true; | ||
186 | } | ||
187 | if (synchronize) | ||
188 | reteContainer.connectAndSynchronize(this, receiver); | ||
189 | else | ||
190 | reteContainer.connect(this, receiver); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * Connects a new external receiver that will receive update notifications from now on. The receiver will | ||
195 | * practically connect to the production node, the added value is unwrapping the updates for external use. | ||
196 | * | ||
197 | * The external receiver will be disconnectable later based on its tag. | ||
198 | * | ||
199 | * @param tag | ||
200 | * an identifier to recognize the child node by. | ||
201 | * | ||
202 | * @param synchronize | ||
203 | * if true, the contents of the production node will be inserted into the receiver after the connection | ||
204 | * is established. | ||
205 | * | ||
206 | */ | ||
207 | public synchronized void connect(Receiver receiver, Object tag, boolean synchronize) { | ||
208 | taggedChildren.put(tag, receiver); | ||
209 | connect(receiver, synchronize); | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * Disconnects a child node. | ||
214 | */ | ||
215 | public synchronized void disconnect(Receiver receiver) { | ||
216 | reteContainer.disconnect(this, receiver); | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * Disconnects the child node that was connected by specifying the given tag. | ||
221 | * | ||
222 | * @return if a child node was found registered with this tag. | ||
223 | */ | ||
224 | public synchronized boolean disconnectByTag(Object tag) { | ||
225 | final Receiver receiver = taggedChildren.remove(tag); | ||
226 | final boolean found = receiver != null; | ||
227 | if (found) | ||
228 | disconnect(receiver); | ||
229 | return found; | ||
230 | } | ||
231 | |||
232 | @Override | ||
233 | protected Tuple transform(Tuple input) { | ||
234 | return context.unwrapTuple(input); | ||
235 | } | ||
236 | |||
237 | abstract class AbstractMatchFetcher implements Runnable { | ||
238 | Indexer indexer; | ||
239 | Tuple signature; | ||
240 | |||
241 | public AbstractMatchFetcher(Indexer indexer, Tuple signature) { | ||
242 | super(); | ||
243 | this.indexer = indexer; | ||
244 | this.signature = signature; | ||
245 | } | ||
246 | |||
247 | @Override | ||
248 | public void run() { | ||
249 | fetch(indexer.get(signature)); | ||
250 | } | ||
251 | |||
252 | protected abstract void fetch(Collection<Tuple> matches); | ||
253 | |||
254 | } | ||
255 | |||
256 | class AllMatchFetcher extends AbstractMatchFetcher { | ||
257 | |||
258 | public AllMatchFetcher(Indexer indexer, Tuple signature) { | ||
259 | super(indexer, signature); | ||
260 | } | ||
261 | |||
262 | Stream<Tuple> matches = null; | ||
263 | |||
264 | public Stream<Tuple> getMatches() { | ||
265 | return matches; | ||
266 | } | ||
267 | |||
268 | @Override | ||
269 | protected void fetch(Collection<Tuple> matches) { | ||
270 | if (matches == null) | ||
271 | this.matches = Stream.of(); | ||
272 | else { | ||
273 | this.matches = matches.stream().map(context::unwrapTuple); | ||
274 | } | ||
275 | |||
276 | } | ||
277 | |||
278 | } | ||
279 | |||
280 | class SingleMatchFetcher extends AbstractMatchFetcher { | ||
281 | |||
282 | public SingleMatchFetcher(Indexer indexer, Tuple signature) { | ||
283 | super(indexer, signature); | ||
284 | } | ||
285 | |||
286 | Tuple match = null; | ||
287 | |||
288 | public Tuple getMatch() { | ||
289 | return match; | ||
290 | } | ||
291 | |||
292 | @Override | ||
293 | protected void fetch(Collection<Tuple> matches) { | ||
294 | if (matches != null && !matches.isEmpty()) | ||
295 | match = context.unwrapTuple(matches.iterator().next()); | ||
296 | } | ||
297 | |||
298 | // public void run() { | ||
299 | // Collection<Tuple> unscopedMatches = indexer.get(signature); | ||
300 | // | ||
301 | // // checking scopes | ||
302 | // if (unscopedMatches != null) { | ||
303 | // for (Tuple um : /* productionNode */unscopedMatches) { | ||
304 | // match = inputConnector.unwrapTuple(um); | ||
305 | // return; | ||
306 | // | ||
307 | // // Tuple ps = inputConnector.unwrapTuple(um); | ||
308 | // // boolean ok = true; | ||
309 | // // if (!ignoreScope) for (int k = 0; (k < ps.getSize()) && ok; k++) { | ||
310 | // // if (pcs[k].getParameterMode() == ParameterMode.INPUT) { | ||
311 | // // // ok = ok && (inputMapping[k]==ps.elements[k]); | ||
312 | // // // should now be true | ||
313 | // // } else // ParameterMode.OUTPUT | ||
314 | // // { | ||
315 | // // IEntity scopeParent = (IEntity) pcs[k].getParameterScope().getParent(); | ||
316 | // // Integer containmentMode = pcs[k].getParameterScope().getContainmentMode(); | ||
317 | // // if (containmentMode == Scope.BELOW) | ||
318 | // // ok = ok && ((IModelElement) ps.get(k)).isBelowNamespace(scopeParent); | ||
319 | // // else | ||
320 | // // /* case Scope.IN: */ | ||
321 | // // ok = ok && scopeParent.equals(((IModelElement) ps.get(k)).getNamespace()); | ||
322 | // // // note: getNamespace returns null instead of the | ||
323 | // // // (imaginary) modelspace root entity for top level | ||
324 | // // // elements; | ||
325 | // // // this is not a problem here as Scope.IN implies | ||
326 | // // // scopeParent != root. | ||
327 | // // | ||
328 | // // } | ||
329 | // // } | ||
330 | // // | ||
331 | // // if (ok) { | ||
332 | // // reteMatching = new ReteMatching(ps, posMapping); | ||
333 | // // return; | ||
334 | // // } | ||
335 | // } | ||
336 | // } | ||
337 | // | ||
338 | // } | ||
339 | |||
340 | } | ||
341 | |||
342 | class CountFetcher extends AbstractMatchFetcher { | ||
343 | |||
344 | public CountFetcher(Indexer indexer, Tuple signature) { | ||
345 | super(indexer, signature); | ||
346 | } | ||
347 | |||
348 | int count = 0; | ||
349 | |||
350 | public int getCount() { | ||
351 | return count; | ||
352 | } | ||
353 | |||
354 | @Override | ||
355 | protected void fetch(Collection<Tuple> matches) { | ||
356 | count = matches == null ? 0 : matches.size(); | ||
357 | } | ||
358 | |||
359 | } | ||
360 | |||
361 | class ProjectionSizeFetcher implements Runnable { | ||
362 | IterableIndexer indexer; | ||
363 | int size = 0; | ||
364 | |||
365 | public ProjectionSizeFetcher(IterableIndexer indexer) { | ||
366 | super(); | ||
367 | this.indexer = indexer; | ||
368 | } | ||
369 | |||
370 | @Override | ||
371 | public void run() { | ||
372 | size = indexer.getBucketCount(); | ||
373 | } | ||
374 | |||
375 | public int getSize() { | ||
376 | return size; | ||
377 | } | ||
378 | |||
379 | } | ||
380 | |||
381 | private boolean[] notNull(Object[] parameters) { | ||
382 | boolean[] notNull = new boolean[parameters.length]; | ||
383 | for (int i = 0; i < parameters.length; ++i) | ||
384 | notNull[i] = parameters[i] != null; | ||
385 | return notNull; | ||
386 | } | ||
387 | |||
388 | |||
389 | |||
390 | @Override | ||
391 | public boolean hasMatch(Object[] parameters) { | ||
392 | return countMatches(parameters) > 0; | ||
393 | } | ||
394 | |||
395 | @Override | ||
396 | public boolean hasMatch(TupleMask parameterSeedMask, ITuple parameters) { | ||
397 | return count(parameterSeedMask, parameters) > 0; | ||
398 | } | ||
399 | |||
400 | @Override | ||
401 | public int countMatches(Object[] parameters) { | ||
402 | return count(parameters, notNull(parameters)); | ||
403 | } | ||
404 | |||
405 | @Override | ||
406 | public int countMatches(TupleMask parameterSeedMask, ITuple parameters) { | ||
407 | return count(parameterSeedMask, parameters); | ||
408 | } | ||
409 | |||
410 | |||
411 | @Override | ||
412 | public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
413 | return Optional.of((long)projectionSize(groupMask)); // always accurate | ||
414 | } | ||
415 | |||
416 | @Override | ||
417 | public Optional<Tuple> getOneArbitraryMatch(Object[] parameters) { | ||
418 | return matchOne(parameters, notNull(parameters)); | ||
419 | } | ||
420 | |||
421 | @Override | ||
422 | public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters) { | ||
423 | return matchOne(parameterSeedMask, parameters); | ||
424 | } | ||
425 | |||
426 | @Override | ||
427 | public Stream<Tuple> getAllMatches(Object[] parameters) { | ||
428 | return matchAll(parameters, notNull(parameters)); | ||
429 | } | ||
430 | |||
431 | @Override | ||
432 | public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters) { | ||
433 | return matchAll(parameterSeedMask, parameters); | ||
434 | } | ||
435 | |||
436 | @Override | ||
437 | public IQueryBackend getQueryBackend() { | ||
438 | return engine; | ||
439 | } | ||
440 | |||
441 | @Override | ||
442 | public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow) { | ||
443 | // As a listener is added as a delayed command, they should be executed to make sure everything is consistent on | ||
444 | // return, see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=562369 | ||
445 | engine.constructionWrapper(() -> { | ||
446 | final CallbackNode callbackNode = new CallbackNode(this.reteContainer, listener); | ||
447 | connect(callbackNode, listenerTag, fireNow); | ||
448 | return null; | ||
449 | }); | ||
450 | } | ||
451 | |||
452 | @Override | ||
453 | public void removeUpdateListener(Object listenerTag) { | ||
454 | engine.constructionWrapper(() -> { | ||
455 | disconnectByTag(listenerTag); | ||
456 | return null; | ||
457 | }); | ||
458 | } | ||
459 | |||
460 | public Indexer getInternalIndexer(TupleMask mask) { | ||
461 | return engine.accessProjection(productionNodeTrace, mask); | ||
462 | } | ||
463 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java new file mode 100644 index 00000000..876ddc99 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.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.rete.matcher; | ||
10 | |||
11 | /** | ||
12 | * Configuration of timely evaluation. | ||
13 | * | ||
14 | * @author Tamas Szabo | ||
15 | * @since 2.4 | ||
16 | */ | ||
17 | public class TimelyConfiguration { | ||
18 | |||
19 | private final AggregatorArchitecture aggregatorArchitecture; | ||
20 | private final TimelineRepresentation timelineRepresentation; | ||
21 | |||
22 | public TimelyConfiguration(final TimelineRepresentation timelineRepresentation, | ||
23 | final AggregatorArchitecture aggregatorArchitecture) { | ||
24 | this.aggregatorArchitecture = aggregatorArchitecture; | ||
25 | this.timelineRepresentation = timelineRepresentation; | ||
26 | } | ||
27 | |||
28 | public AggregatorArchitecture getAggregatorArchitecture() { | ||
29 | return aggregatorArchitecture; | ||
30 | } | ||
31 | |||
32 | public TimelineRepresentation getTimelineRepresentation() { | ||
33 | return timelineRepresentation; | ||
34 | } | ||
35 | |||
36 | public enum AggregatorArchitecture { | ||
37 | /** | ||
38 | * Aggregands are copied over from lower timestamps to higher timestamps. | ||
39 | */ | ||
40 | PARALLEL, | ||
41 | |||
42 | /** | ||
43 | * Aggregands are only present at the timestamp where they are inserted at. | ||
44 | * Only aggregate results are pushed towards higher timestamps during folding. | ||
45 | */ | ||
46 | SEQUENTIAL | ||
47 | } | ||
48 | |||
49 | public enum TimelineRepresentation { | ||
50 | /** | ||
51 | * Only the first moment (timestamp) of appearance is maintained per tuple. | ||
52 | */ | ||
53 | FIRST_ONLY, | ||
54 | |||
55 | /** | ||
56 | * Complete timeline (series of appearance & disappearance) is maintained per tuple. | ||
57 | */ | ||
58 | FAITHFUL | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java new file mode 100644 index 00000000..2777f169 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java | |||
@@ -0,0 +1,64 @@ | |||
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.rete.matcher; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; | ||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
13 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture; | ||
14 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; | ||
15 | |||
16 | /** | ||
17 | * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use non-scattered timely | ||
18 | * evaluation. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * @since 2.4 | ||
22 | */ | ||
23 | public class TimelyReteBackendFactory extends ReteBackendFactory { | ||
24 | |||
25 | private final TimelyConfiguration configuration; | ||
26 | |||
27 | public static final TimelyReteBackendFactory FIRST_ONLY_SEQUENTIAL = new TimelyReteBackendFactory( | ||
28 | new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.SEQUENTIAL)); | ||
29 | public static final TimelyReteBackendFactory FIRST_ONLY_PARALLEL = new TimelyReteBackendFactory( | ||
30 | new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.PARALLEL)); | ||
31 | public static final TimelyReteBackendFactory FAITHFUL_SEQUENTIAL = new TimelyReteBackendFactory( | ||
32 | new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.SEQUENTIAL)); | ||
33 | public static final TimelyReteBackendFactory FAITHFUL_PARALLEL = new TimelyReteBackendFactory( | ||
34 | new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.PARALLEL)); | ||
35 | |||
36 | public TimelyReteBackendFactory(final TimelyConfiguration configuration) { | ||
37 | this.configuration = configuration; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public IQueryBackend create(final IQueryBackendContext context) { | ||
42 | return create(context, false, configuration); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public int hashCode() { | ||
47 | return TimelyReteBackendFactory.class.hashCode(); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public boolean equals(final Object obj) { | ||
52 | if (this == obj) { | ||
53 | return true; | ||
54 | } | ||
55 | if (obj == null) { | ||
56 | return false; | ||
57 | } | ||
58 | if (!(obj instanceof TimelyReteBackendFactory)) { | ||
59 | return false; | ||
60 | } | ||
61 | return true; | ||
62 | } | ||
63 | |||
64 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java new file mode 100644 index 00000000..4aef0f96 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java | |||
@@ -0,0 +1,43 @@ | |||
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.rete.misc; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.LinkedList; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
18 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
19 | |||
20 | /** | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | * A bag is a container that tuples can be dumped into. Does NOT propagate updates! Optimized for small contents | ||
24 | * size OR positive updates only. | ||
25 | */ | ||
26 | public class Bag extends SimpleReceiver { | ||
27 | |||
28 | public Collection<Tuple> contents; | ||
29 | |||
30 | public Bag(ReteContainer reteContainer) { | ||
31 | super(reteContainer); | ||
32 | contents = new LinkedList<Tuple>(); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
37 | if (direction == Direction.INSERT) | ||
38 | contents.add(updateElement); | ||
39 | else | ||
40 | contents.remove(updateElement); | ||
41 | } | ||
42 | |||
43 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java new file mode 100644 index 00000000..980d3eee --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java | |||
@@ -0,0 +1,50 @@ | |||
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.rete.misc; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | |||
22 | /** | ||
23 | * Node that always contains a single constant Tuple | ||
24 | * | ||
25 | * @author Gabor Bergmann | ||
26 | */ | ||
27 | public class ConstantNode extends StandardNode { | ||
28 | |||
29 | protected Tuple constant; | ||
30 | |||
31 | /** | ||
32 | * @param constant | ||
33 | * will be wrapped using {@link IQueryRuntimeContext#wrapTuple(Tuple)} | ||
34 | */ | ||
35 | public ConstantNode(ReteContainer reteContainer, Tuple constant) { | ||
36 | super(reteContainer); | ||
37 | this.constant = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapTuple(constant); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public void pullInto(Collection<Tuple> collector, boolean flush) { | ||
42 | collector.add(constant); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
47 | collector.put(constant, Timestamp.INSERT_AT_ZERO_TIMELINE); | ||
48 | } | ||
49 | |||
50 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java new file mode 100644 index 00000000..efba3117 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java | |||
@@ -0,0 +1,43 @@ | |||
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.rete.misc; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.rete.network.Network; | ||
14 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
15 | |||
16 | /** | ||
17 | * Default configuration for DeltaMonitor. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public class DefaultDeltaMonitor extends DeltaMonitor<Tuple> { | ||
23 | |||
24 | /** | ||
25 | * @param reteContainer | ||
26 | */ | ||
27 | public DefaultDeltaMonitor(ReteContainer reteContainer) { | ||
28 | super(reteContainer); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * @param network | ||
33 | */ | ||
34 | public DefaultDeltaMonitor(Network network) { | ||
35 | super(network.getHeadContainer()); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public Tuple statelessConvert(Tuple tuple) { | ||
40 | return tuple; | ||
41 | } | ||
42 | |||
43 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java new file mode 100644 index 00000000..82b6ecda --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java | |||
@@ -0,0 +1,111 @@ | |||
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.rete.misc; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.LinkedHashSet; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | |||
21 | /** | ||
22 | * A monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state | ||
23 | * acknowledged by the client. Match tuples are represented by a type MatchType. | ||
24 | * | ||
25 | * <p> | ||
26 | * <b>Usage</b>. If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that | ||
27 | * particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can | ||
28 | * be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the | ||
29 | * matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be | ||
30 | * acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to | ||
31 | * matchFoundEvents. | ||
32 | * | ||
33 | * <p> | ||
34 | * <b>Technical notes</b>. Does NOT propagate updates! | ||
35 | * | ||
36 | * By overriding statelessConvert(), results can be stored to a MatchType. MatchType must provide equals() and | ||
37 | * hashCode() reflecting its contents. The default implementation (DefaultDeltaMonitor) uses Tuple as MatchType. | ||
38 | * | ||
39 | * By overriding statelessFilter(), some tuples can be filtered. | ||
40 | * | ||
41 | * @author Gabor Bergmann | ||
42 | * | ||
43 | */ | ||
44 | public abstract class DeltaMonitor<MatchType> extends SimpleReceiver implements Clearable { | ||
45 | |||
46 | /** | ||
47 | * matches that are newly found | ||
48 | */ | ||
49 | public Collection<MatchType> matchFoundEvents; | ||
50 | /** | ||
51 | * matches that are newly lost | ||
52 | */ | ||
53 | public Collection<MatchType> matchLostEvents; | ||
54 | |||
55 | /** | ||
56 | * @param reteContainer | ||
57 | */ | ||
58 | public DeltaMonitor(ReteContainer reteContainer) { | ||
59 | super(reteContainer); | ||
60 | matchFoundEvents = new LinkedHashSet<MatchType>(); | ||
61 | matchLostEvents = new LinkedHashSet<MatchType>(); | ||
62 | reteContainer.registerClearable(this); | ||
63 | } | ||
64 | |||
65 | // /** | ||
66 | // * Build a delta monitor into the head container of the network. | ||
67 | // * | ||
68 | // * @param network | ||
69 | // */ | ||
70 | // public DeltaMonitor(Network network) { | ||
71 | // this(network.getHeadContainer()); | ||
72 | // } | ||
73 | |||
74 | /** | ||
75 | * Override this method to provide a lightweight, stateless filter on the tuples | ||
76 | * | ||
77 | * @param tuple | ||
78 | * the occurrence that is to be filtered | ||
79 | * @return true if this tuple should be monitored, false if ignored | ||
80 | */ | ||
81 | public boolean statelessFilter(Tuple tuple) { | ||
82 | return true; | ||
83 | } | ||
84 | |||
85 | public abstract MatchType statelessConvert(Tuple tuple); | ||
86 | |||
87 | @Override | ||
88 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
89 | if (statelessFilter(updateElement)) { | ||
90 | MatchType match = statelessConvert(updateElement); | ||
91 | if (direction == Direction.INSERT) { | ||
92 | if (!matchLostEvents.remove(match)) // either had before but | ||
93 | // lost | ||
94 | matchFoundEvents.add(match); // or brand-new | ||
95 | } else // revoke | ||
96 | { | ||
97 | if (!matchFoundEvents.remove(match)) // either never found | ||
98 | // in the first | ||
99 | // place | ||
100 | matchLostEvents.add(match); // or newly lost | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public void clear() { | ||
107 | matchFoundEvents.clear(); | ||
108 | matchLostEvents.clear(); | ||
109 | } | ||
110 | |||
111 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java new file mode 100644 index 00000000..dcf9ae78 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java | |||
@@ -0,0 +1,109 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, 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.rete.misc; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.rete.network.BaseNode; | ||
15 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
16 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
17 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
18 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
19 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
20 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
21 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
22 | |||
23 | /** | ||
24 | * @author Bergmann Gabor | ||
25 | * | ||
26 | */ | ||
27 | public abstract class SimpleReceiver extends BaseNode implements Receiver { | ||
28 | |||
29 | protected Supplier parent = null; | ||
30 | /** | ||
31 | * @since 1.6 | ||
32 | */ | ||
33 | protected final Mailbox mailbox; | ||
34 | |||
35 | /** | ||
36 | * @param reteContainer | ||
37 | */ | ||
38 | public SimpleReceiver(ReteContainer reteContainer) { | ||
39 | super(reteContainer); | ||
40 | mailbox = instantiateMailbox(); | ||
41 | reteContainer.registerClearable(mailbox); | ||
42 | } | ||
43 | |||
44 | /** | ||
45 | * Instantiates the {@link Mailbox} of this receiver. | ||
46 | * Subclasses may override this method to provide their own mailbox implementation. | ||
47 | * | ||
48 | * @return the mailbox | ||
49 | * @since 2.0 | ||
50 | */ | ||
51 | protected Mailbox instantiateMailbox() { | ||
52 | if (this.reteContainer.isTimelyEvaluation()) { | ||
53 | return new TimelyMailbox(this, this.reteContainer); | ||
54 | } else { | ||
55 | return new BehaviorChangingMailbox(this, this.reteContainer); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public Mailbox getMailbox() { | ||
61 | return this.mailbox; | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public void appendParent(Supplier supplier) { | ||
66 | if (parent == null) | ||
67 | parent = supplier; | ||
68 | else | ||
69 | throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent | ||
70 | + ") and cannot connect to additional parent (" + supplier | ||
71 | + ") as it is not a Uniqueness Enforcer Node. "); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void removeParent(Supplier supplier) { | ||
76 | if (parent == supplier) | ||
77 | parent = null; | ||
78 | else | ||
79 | throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not " | ||
80 | + supplier); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public Collection<Supplier> getParents() { | ||
85 | if (parent == null) | ||
86 | return Collections.emptySet(); | ||
87 | else | ||
88 | return Collections.singleton(parent); | ||
89 | } | ||
90 | |||
91 | /** | ||
92 | * Disconnects this node from the network. Can be called publicly. | ||
93 | * | ||
94 | * @pre: child nodes, if any, must already be disconnected. | ||
95 | */ | ||
96 | public void disconnectFromNetwork() { | ||
97 | if (parent != null) | ||
98 | reteContainer.disconnect(parent, this); | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public void assignTraceInfo(TraceInfo traceInfo) { | ||
103 | super.assignTraceInfo(traceInfo); | ||
104 | if (traceInfo.propagateFromStandardNodeToSupplierParent()) | ||
105 | if (parent != null) | ||
106 | parent.acceptPropagatedTraceInfo(traceInfo); | ||
107 | } | ||
108 | |||
109 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java new file mode 100644 index 00000000..2469d6bd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java | |||
@@ -0,0 +1,108 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, 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.rete.network; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.TreeSet; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.rete.traceability.PatternTraceInfo; | ||
17 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
18 | |||
19 | /** | ||
20 | * Base implementation for a Rete node. | ||
21 | * | ||
22 | * @author Bergmann Gabor | ||
23 | * | ||
24 | */ | ||
25 | public abstract class BaseNode implements Node { | ||
26 | |||
27 | protected ReteContainer reteContainer; | ||
28 | protected long nodeId; | ||
29 | protected Object tag; | ||
30 | protected Set<TraceInfo> traceInfos; | ||
31 | |||
32 | /** | ||
33 | * @param reteContainer | ||
34 | * the container to create this node in | ||
35 | */ | ||
36 | public BaseNode(ReteContainer reteContainer) { | ||
37 | super(); | ||
38 | this.reteContainer = reteContainer; | ||
39 | this.nodeId = reteContainer.registerNode(this); | ||
40 | this.traceInfos = new HashSet<TraceInfo>(); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public String toString() { | ||
45 | if (tag != null) | ||
46 | return toStringCore() + "->" + getTraceInfoPatternsEnumerated() + "{" + tag.toString() + "}"; | ||
47 | else | ||
48 | return toStringCore() + "->" + getTraceInfoPatternsEnumerated(); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * clients should override this to append before the tag / trace indicators | ||
53 | */ | ||
54 | protected String toStringCore() { | ||
55 | return "[" + nodeId + "]" + getClass().getSimpleName(); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public ReteContainer getContainer() { | ||
60 | return reteContainer; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public long getNodeId() { | ||
65 | return nodeId; | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Object getTag() { | ||
70 | return tag; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public void setTag(Object tag) { | ||
75 | this.tag = tag; | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public Set<TraceInfo> getTraceInfos() { | ||
80 | return Collections.unmodifiableSet(traceInfos); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public void assignTraceInfo(TraceInfo traceInfo) { | ||
85 | traceInfos.add(traceInfo); | ||
86 | traceInfo.assignNode(this); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public void acceptPropagatedTraceInfo(TraceInfo traceInfo) { | ||
91 | assignTraceInfo(traceInfo); | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Descendants should use this in e.g. logging | ||
96 | */ | ||
97 | protected String getTraceInfoPatternsEnumerated() { | ||
98 | TreeSet<String> patternNames = new TreeSet<String>(); | ||
99 | for (TraceInfo trInfo : traceInfos) { | ||
100 | if (trInfo instanceof PatternTraceInfo) { | ||
101 | final String pName = ((PatternTraceInfo) trInfo).getPatternName(); | ||
102 | patternNames.add(pName); | ||
103 | } | ||
104 | } | ||
105 | return patternNames.toString(); | ||
106 | } | ||
107 | |||
108 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java new file mode 100644 index 00000000..b261d19d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java | |||
@@ -0,0 +1,171 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | package tools.refinery.viatra.runtime.rete.network; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.rete.aggregation.IndexerBasedAggregatorNode; | ||
14 | import tools.refinery.viatra.runtime.rete.boundary.InputConnector; | ||
15 | import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode; | ||
16 | import tools.refinery.viatra.runtime.rete.index.DualInputNode; | ||
17 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
18 | import tools.refinery.viatra.runtime.rete.index.IterableIndexer; | ||
19 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
20 | import tools.refinery.viatra.runtime.rete.recipes.*; | ||
21 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
22 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
23 | |||
24 | import java.util.ArrayList; | ||
25 | import java.util.Collection; | ||
26 | import java.util.List; | ||
27 | |||
28 | /** | ||
29 | * Class responsible for connecting freshly instantiating Rete nodes to their parents. | ||
30 | * | ||
31 | * @author Bergmann Gabor | ||
32 | * | ||
33 | */ | ||
34 | class ConnectionFactory { | ||
35 | ReteContainer reteContainer; | ||
36 | |||
37 | public ConnectionFactory(ReteContainer reteContainer) { | ||
38 | super(); | ||
39 | this.reteContainer = reteContainer; | ||
40 | } | ||
41 | |||
42 | // TODO move to node implementation instead? | ||
43 | private boolean isStateful(ReteNodeRecipe recipe) { | ||
44 | return recipe instanceof ProjectionIndexerRecipe || recipe instanceof IndexerBasedAggregatorRecipe | ||
45 | || recipe instanceof SingleColumnAggregatorRecipe || recipe instanceof ExpressionEnforcerRecipe | ||
46 | || recipe instanceof TransitiveClosureRecipe || recipe instanceof ProductionRecipe | ||
47 | || recipe instanceof UniquenessEnforcerRecipe || recipe instanceof RelationEvaluationRecipe; | ||
48 | |||
49 | } | ||
50 | |||
51 | /** | ||
52 | * PRE: nodes for parent recipes must already be created and registered | ||
53 | * <p> | ||
54 | * PRE: must not be an input node (for which {@link InputConnector} is responsible) | ||
55 | */ | ||
56 | public void connectToParents(RecipeTraceInfo recipeTrace, Node freshNode) { | ||
57 | final ReteNodeRecipe recipe = recipeTrace.getRecipe(); | ||
58 | if (recipe instanceof ConstantRecipe) { | ||
59 | // NO-OP | ||
60 | } else if (recipe instanceof InputRecipe) { | ||
61 | throw new IllegalArgumentException( | ||
62 | ConnectionFactory.class.getSimpleName() + " not intended for input connection: " + recipe); | ||
63 | } else if (recipe instanceof SingleParentNodeRecipe) { | ||
64 | final Receiver receiver = (Receiver) freshNode; | ||
65 | ReteNodeRecipe parentRecipe = ((SingleParentNodeRecipe) recipe).getParent(); | ||
66 | connectToParent(recipe, receiver, parentRecipe); | ||
67 | } else if (recipe instanceof RelationEvaluationRecipe) { | ||
68 | List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents(); | ||
69 | List<Supplier> parentSuppliers = new ArrayList<Supplier>(); | ||
70 | for (final ReteNodeRecipe parentRecipe : parentRecipes) { | ||
71 | parentSuppliers.add(getSupplierForRecipe(parentRecipe)); | ||
72 | } | ||
73 | ((RelationEvaluatorNode) freshNode).connectToParents(parentSuppliers); | ||
74 | } else if (recipe instanceof BetaRecipe) { | ||
75 | final DualInputNode beta = (DualInputNode) freshNode; | ||
76 | final ArrayList<RecipeTraceInfo> parentTraces = new ArrayList<RecipeTraceInfo>( | ||
77 | recipeTrace.getParentRecipeTraces()); | ||
78 | Slots slots = avoidActiveNodeConflict(parentTraces.get(0), parentTraces.get(1)); | ||
79 | beta.connectToIndexers(slots.primary, slots.secondary); | ||
80 | } else if (recipe instanceof IndexerBasedAggregatorRecipe) { | ||
81 | final IndexerBasedAggregatorNode aggregator = (IndexerBasedAggregatorNode) freshNode; | ||
82 | final IndexerBasedAggregatorRecipe aggregatorRecipe = (IndexerBasedAggregatorRecipe) recipe; | ||
83 | aggregator.initializeWith((ProjectionIndexer) resolveIndexer(aggregatorRecipe.getParent())); | ||
84 | } else if (recipe instanceof MultiParentNodeRecipe) { | ||
85 | final Receiver receiver = (Receiver) freshNode; | ||
86 | List<ReteNodeRecipe> parentRecipes = ((MultiParentNodeRecipe) recipe).getParents(); | ||
87 | for (ReteNodeRecipe parentRecipe : parentRecipes) { | ||
88 | connectToParent(recipe, receiver, parentRecipe); | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | private Indexer resolveIndexer(final IndexerRecipe indexerRecipe) { | ||
94 | final Address<? extends Node> address = reteContainer.getNetwork().getExistingNodeByRecipe(indexerRecipe); | ||
95 | return (Indexer) reteContainer.resolveLocal(address); | ||
96 | } | ||
97 | |||
98 | private void connectToParent(ReteNodeRecipe recipe, Receiver freshNode, ReteNodeRecipe parentRecipe) { | ||
99 | final Supplier parentSupplier = getSupplierForRecipe(parentRecipe); | ||
100 | |||
101 | // special synch | ||
102 | if (freshNode instanceof ReinitializedNode) { | ||
103 | Collection<Tuple> tuples = new ArrayList<Tuple>(); | ||
104 | parentSupplier.pullInto(tuples, true); | ||
105 | ((ReinitializedNode) freshNode).reinitializeWith(tuples); | ||
106 | reteContainer.connect(parentSupplier, freshNode); | ||
107 | } else { // default case | ||
108 | // stateless nodes do not have to be synced with contents UNLESS they already have children (recursive | ||
109 | // corner case) | ||
110 | if (isStateful(recipe) | ||
111 | || ((freshNode instanceof Supplier) && !((Supplier) freshNode).getReceivers().isEmpty())) { | ||
112 | reteContainer.connectAndSynchronize(parentSupplier, freshNode); | ||
113 | } else { | ||
114 | // stateless node, no synch | ||
115 | reteContainer.connect(parentSupplier, freshNode); | ||
116 | } | ||
117 | } | ||
118 | } | ||
119 | |||
120 | private Supplier getSupplierForRecipe(ReteNodeRecipe recipe) { | ||
121 | @SuppressWarnings("unchecked") | ||
122 | final Address<? extends Supplier> parentAddress = (Address<? extends Supplier>) reteContainer.getNetwork() | ||
123 | .getExistingNodeByRecipe(recipe); | ||
124 | final Supplier supplier = reteContainer.getProvisioner().asSupplier(parentAddress); | ||
125 | return supplier; | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of | ||
130 | * the two indexers is supported. | ||
131 | * | ||
132 | * @return a replacement for the secondary Indexers, if needed | ||
133 | */ | ||
134 | private Slots avoidActiveNodeConflict(final RecipeTraceInfo primarySlot, final RecipeTraceInfo secondarySlot) { | ||
135 | Slots result = new Slots() { | ||
136 | { | ||
137 | primary = (IterableIndexer) resolveIndexer((ProjectionIndexerRecipe) primarySlot.getRecipe()); | ||
138 | secondary = resolveIndexer((IndexerRecipe) secondarySlot.getRecipe()); | ||
139 | } | ||
140 | }; | ||
141 | if (activeNodeConflict(result.primary, result.secondary)) | ||
142 | if (result.secondary instanceof IterableIndexer) | ||
143 | result.secondary = resolveActiveIndexer(secondarySlot); | ||
144 | else | ||
145 | result.primary = (IterableIndexer) resolveActiveIndexer(primarySlot); | ||
146 | return result; | ||
147 | } | ||
148 | |||
149 | private Indexer resolveActiveIndexer(final RecipeTraceInfo inactiveIndexerTrace) { | ||
150 | final RecipeTraceInfo activeIndexerTrace = reteContainer.getProvisioner() | ||
151 | .accessActiveIndexer(inactiveIndexerTrace); | ||
152 | reteContainer.getProvisioner().getOrCreateNodeByRecipe(activeIndexerTrace); | ||
153 | return resolveIndexer((ProjectionIndexerRecipe) activeIndexerTrace.getRecipe()); | ||
154 | } | ||
155 | |||
156 | private static class Slots { | ||
157 | IterableIndexer primary; | ||
158 | Indexer secondary; | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of | ||
163 | * the two indexers is supported. | ||
164 | * | ||
165 | * @return true if there is a conflict of active nodes. | ||
166 | */ | ||
167 | private boolean activeNodeConflict(Indexer primarySlot, Indexer secondarySlot) { | ||
168 | return !primarySlot.equals(secondarySlot) && primarySlot.getActiveNode().equals(secondarySlot.getActiveNode()); | ||
169 | } | ||
170 | |||
171 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java new file mode 100644 index 00000000..c22b06d8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java | |||
@@ -0,0 +1,31 @@ | |||
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.rete.network; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
12 | |||
13 | /** | ||
14 | * @author Gabor Bergmann | ||
15 | * @since 1.7 | ||
16 | */ | ||
17 | public interface IGroupable { | ||
18 | |||
19 | /** | ||
20 | * @return the current group of the mailbox | ||
21 | * @since 1.7 | ||
22 | */ | ||
23 | CommunicationGroup getCurrentGroup(); | ||
24 | |||
25 | /** | ||
26 | * Sets the current group of the mailbox | ||
27 | * @since 1.7 | ||
28 | */ | ||
29 | void setCurrentGroup(CommunicationGroup group); | ||
30 | |||
31 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java new file mode 100644 index 00000000..64f59ff3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java | |||
@@ -0,0 +1,408 @@ | |||
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.rete.network; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collection; | ||
14 | import java.util.Collections; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Map.Entry; | ||
18 | import java.util.Set; | ||
19 | import java.util.concurrent.locks.Lock; | ||
20 | import java.util.concurrent.locks.ReadWriteLock; | ||
21 | import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
22 | |||
23 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
24 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
25 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
26 | import tools.refinery.viatra.runtime.rete.boundary.InputConnector; | ||
27 | import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; | ||
28 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
29 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
30 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
31 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
32 | |||
33 | /** | ||
34 | * @author Gabor Bergmann | ||
35 | * | ||
36 | */ | ||
37 | public class Network { | ||
38 | final int threads; | ||
39 | |||
40 | protected ArrayList<ReteContainer> containers; | ||
41 | ReteContainer headContainer; | ||
42 | private int firstContainer = 0; | ||
43 | private int nextContainer = 0; | ||
44 | |||
45 | // the following fields exist only if threads > 0 | ||
46 | protected Map<ReteContainer, Long> globalTerminationCriteria = null; | ||
47 | protected Map<ReteContainer, Long> reportedClocks = null; | ||
48 | protected Lock updateLock = null; // grab during normal update operations | ||
49 | protected Lock structuralChangeLock = null; // grab if the network structure | ||
50 | // is to | ||
51 | // be changed | ||
52 | |||
53 | // Knowledge of the outside world | ||
54 | private ReteEngine engine; | ||
55 | protected NodeFactory nodeFactory; | ||
56 | protected InputConnector inputConnector; | ||
57 | |||
58 | // Node and recipe administration | ||
59 | // incl. addresses for existing nodes by recipe (where available) | ||
60 | // Maintained by NodeProvisioner of each container | ||
61 | Map<ReteNodeRecipe, Address<? extends Node>> nodesByRecipe = CollectionsFactory.createMap(); | ||
62 | Set<RecipeTraceInfo> recipeTraces = CollectionsFactory.createSet(); | ||
63 | |||
64 | /** | ||
65 | * @throws IllegalStateException | ||
66 | * if no node has been constructed for the recipe | ||
67 | */ | ||
68 | public synchronized Address<? extends Node> getExistingNodeByRecipe(ReteNodeRecipe recipe) { | ||
69 | final Address<? extends Node> node = nodesByRecipe.get(recipe); | ||
70 | if (node == null) | ||
71 | throw new IllegalStateException(String.format("Rete node for recipe %s not constructed yet.", recipe)); | ||
72 | return node; | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * @return null if no node has been constructed for the recipe | ||
77 | */ | ||
78 | public synchronized Address<? extends Node> getNodeByRecipeIfExists(ReteNodeRecipe recipe) { | ||
79 | final Address<? extends Node> node = nodesByRecipe.get(recipe); | ||
80 | return node; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * @param threads | ||
85 | * the number of threads to operate the network with; 0 means single-threaded operation, 1 starts an | ||
86 | * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers. | ||
87 | */ | ||
88 | public Network(int threads, ReteEngine engine) { | ||
89 | super(); | ||
90 | this.threads = threads; | ||
91 | this.engine = engine; | ||
92 | this.inputConnector = new InputConnector(this); | ||
93 | this.nodeFactory = new NodeFactory(engine.getLogger()); | ||
94 | |||
95 | containers = new ArrayList<ReteContainer>(); | ||
96 | firstContainer = (threads > 1) ? Options.firstFreeContainer : 0; // NOPMD | ||
97 | nextContainer = firstContainer; | ||
98 | |||
99 | if (threads > 0) { | ||
100 | globalTerminationCriteria = CollectionsFactory.createMap(); | ||
101 | reportedClocks = CollectionsFactory.createMap(); | ||
102 | ReadWriteLock rwl = new ReentrantReadWriteLock(); | ||
103 | updateLock = rwl.readLock(); | ||
104 | structuralChangeLock = rwl.writeLock(); | ||
105 | for (int i = 0; i < threads; ++i) | ||
106 | containers.add(new ReteContainer(this, true)); | ||
107 | } else | ||
108 | containers.add(new ReteContainer(this, false)); | ||
109 | |||
110 | headContainer = containers.get(0); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Kills this Network along with all containers and message consumption cycles. | ||
115 | */ | ||
116 | public void kill() { | ||
117 | for (ReteContainer container : containers) { | ||
118 | container.kill(); | ||
119 | } | ||
120 | containers.clear(); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Returns the head container, that is guaranteed to reside in the same JVM as the Network object. | ||
125 | */ | ||
126 | public ReteContainer getHeadContainer() { | ||
127 | return headContainer; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Returns the next container in round-robin fashion. Configurable not to yield head container. | ||
132 | */ | ||
133 | public ReteContainer getNextContainer() { | ||
134 | if (nextContainer >= containers.size()) | ||
135 | nextContainer = firstContainer; | ||
136 | return containers.get(nextContainer++); | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * Internal message delivery method. | ||
141 | * | ||
142 | * @pre threads > 0 | ||
143 | */ | ||
144 | private void sendUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) { | ||
145 | ReteContainer affectedContainer = receiver.getContainer(); | ||
146 | synchronized (globalTerminationCriteria) { | ||
147 | long newCriterion = affectedContainer.sendUpdateToLocalAddress(receiver, direction, updateElement); | ||
148 | terminationCriterion(affectedContainer, newCriterion); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Internal message delivery method for single-threaded operation | ||
154 | * | ||
155 | * @pre threads == 0 | ||
156 | */ | ||
157 | private void sendUpdateSingleThreaded(Address<? extends Receiver> receiver, Direction direction, | ||
158 | Tuple updateElement) { | ||
159 | ReteContainer affectedContainer = receiver.getContainer(); | ||
160 | affectedContainer.sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement); | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * Internal message delivery method. | ||
165 | * | ||
166 | * @pre threads > 0 | ||
167 | */ | ||
168 | private void sendUpdates(Address<? extends Receiver> receiver, Direction direction, | ||
169 | Collection<Tuple> updateElements) { | ||
170 | if (updateElements.isEmpty()) | ||
171 | return; | ||
172 | ReteContainer affectedContainer = receiver.getContainer(); | ||
173 | synchronized (globalTerminationCriteria) { | ||
174 | long newCriterion = affectedContainer.sendUpdatesToLocalAddress(receiver, direction, updateElements); | ||
175 | terminationCriterion(affectedContainer, newCriterion); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may | ||
181 | * reside in any of the containers associated with this network. To be called from a user thread during normal | ||
182 | * operation, NOT during construction. | ||
183 | * | ||
184 | * @since 2.4 | ||
185 | */ | ||
186 | public void sendExternalUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) { | ||
187 | if (threads > 0) { | ||
188 | try { | ||
189 | updateLock.lock(); | ||
190 | sendUpdate(receiver, direction, updateElement); | ||
191 | } finally { | ||
192 | updateLock.unlock(); | ||
193 | } | ||
194 | } else { | ||
195 | sendUpdateSingleThreaded(receiver, direction, updateElement); | ||
196 | // getHeadContainer(). | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /** | ||
201 | * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may | ||
202 | * reside in any of the containers associated with this network. To be called from a user thread during | ||
203 | * construction. | ||
204 | * | ||
205 | * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may | ||
206 | * span through network calls, that's why it's not enforced here ) | ||
207 | * | ||
208 | * @return the value of the target container's clock at the time when the message was accepted into its message | ||
209 | * queue | ||
210 | * @since 2.4 | ||
211 | */ | ||
212 | public void sendConstructionUpdate(Address<? extends Receiver> receiver, Direction direction, Tuple updateElement) { | ||
213 | // structuralChangeLock.lock(); | ||
214 | if (threads > 0) | ||
215 | sendUpdate(receiver, direction, updateElement); | ||
216 | else | ||
217 | receiver.getContainer().sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement); | ||
218 | // structuralChangeLock.unlock(); | ||
219 | } | ||
220 | |||
221 | /** | ||
222 | * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial | ||
223 | * matching. The node may reside in any of the containers associated with this network. To be called from a user | ||
224 | * thread during construction. | ||
225 | * | ||
226 | * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may | ||
227 | * span through network calls, that's why it's not enforced here ) | ||
228 | * | ||
229 | * @since 2.4 | ||
230 | */ | ||
231 | public void sendConstructionUpdates(Address<? extends Receiver> receiver, Direction direction, | ||
232 | Collection<Tuple> updateElements) { | ||
233 | // structuralChangeLock.lock(); | ||
234 | if (threads > 0) | ||
235 | sendUpdates(receiver, direction, updateElements); | ||
236 | else | ||
237 | receiver.getContainer().sendUpdatesToLocalAddressSingleThreaded(receiver, direction, updateElements); | ||
238 | // structuralChangeLock.unlock(); | ||
239 | } | ||
240 | |||
241 | /** | ||
242 | * Establishes connection between a supplier and a receiver node, regardless which container they are in. Not to be | ||
243 | * called remotely, because this method enforces the structural lock. | ||
244 | * | ||
245 | * @param supplier | ||
246 | * @param receiver | ||
247 | * @param synchronise | ||
248 | * indicates whether the receiver should be synchronised to the current contents of the supplier | ||
249 | */ | ||
250 | public void connectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver, | ||
251 | boolean synchronise) { | ||
252 | try { | ||
253 | if (threads > 0) | ||
254 | structuralChangeLock.lock(); | ||
255 | receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise); | ||
256 | } finally { | ||
257 | if (threads > 0) | ||
258 | structuralChangeLock.unlock(); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * Severs connection between a supplier and a receiver node, regardless which container they are in. Not to be | ||
264 | * called remotely, because this method enforces the structural lock. | ||
265 | * | ||
266 | * @param supplier | ||
267 | * @param receiver | ||
268 | * @param desynchronise | ||
269 | * indicates whether the current contents of the supplier should be subtracted from the receiver | ||
270 | */ | ||
271 | public void disconnectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver, | ||
272 | boolean desynchronise) { | ||
273 | try { | ||
274 | if (threads > 0) | ||
275 | structuralChangeLock.lock(); | ||
276 | receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise); | ||
277 | } finally { | ||
278 | if (threads > 0) | ||
279 | structuralChangeLock.unlock(); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | /** | ||
284 | * Containers use this method to report whenever they run out of messages in their queue. | ||
285 | * | ||
286 | * To be called from the thread of the reporting container. | ||
287 | * | ||
288 | * @pre threads > 0. | ||
289 | * @param reportingContainer | ||
290 | * the container reporting the emptiness of its message queue. | ||
291 | * @param clock | ||
292 | * the value of the container's clock when reporting. | ||
293 | * @param localTerminationCriteria | ||
294 | * the latest clock values this container has received from other containers since the last time it | ||
295 | * reported termination. | ||
296 | */ | ||
297 | void reportLocalUpdateTermination(ReteContainer reportingContainer, long clock, | ||
298 | Map<ReteContainer, Long> localTerminationCriteria) { | ||
299 | synchronized (globalTerminationCriteria) { | ||
300 | for (Entry<ReteContainer, Long> criterion : localTerminationCriteria.entrySet()) { | ||
301 | terminationCriterion(criterion.getKey(), criterion.getValue()); | ||
302 | } | ||
303 | |||
304 | reportedClocks.put(reportingContainer, clock); | ||
305 | Long criterion = globalTerminationCriteria.get(reportingContainer); | ||
306 | if (criterion != null && criterion < clock) | ||
307 | globalTerminationCriteria.remove(reportingContainer); | ||
308 | |||
309 | if (globalTerminationCriteria.isEmpty()) | ||
310 | globalTerminationCriteria.notifyAll(); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * @pre threads > 0 | ||
316 | */ | ||
317 | private void terminationCriterion(ReteContainer affectedContainer, long newCriterion) { | ||
318 | synchronized (globalTerminationCriteria) { | ||
319 | Long oldCriterion = globalTerminationCriteria.get(affectedContainer); | ||
320 | Long oldClock = reportedClocks.get(affectedContainer); | ||
321 | long relevantClock = oldClock == null ? 0 : oldClock; | ||
322 | if ((relevantClock <= newCriterion) && (oldCriterion == null || oldCriterion < newCriterion)) { | ||
323 | globalTerminationCriteria.put(affectedContainer, newCriterion); | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * Waits until all rete update operations are settled in all containers. Returns immediately, if no updates are | ||
330 | * pending. | ||
331 | * | ||
332 | * To be called from any user thread. | ||
333 | */ | ||
334 | public void waitForReteTermination() { | ||
335 | if (threads > 0) { | ||
336 | synchronized (globalTerminationCriteria) { | ||
337 | while (!globalTerminationCriteria.isEmpty()) { | ||
338 | try { | ||
339 | globalTerminationCriteria.wait(); | ||
340 | } catch (InterruptedException e) { | ||
341 | |||
342 | } | ||
343 | } | ||
344 | } | ||
345 | } else | ||
346 | headContainer.deliverMessagesSingleThreaded(); | ||
347 | } | ||
348 | |||
349 | /** | ||
350 | * Waits to execute action until all rete update operations are settled in all containers. Runs action and returns | ||
351 | * immediately, if no updates are pending. The given action is guaranteed to be run when the terminated state still | ||
352 | * persists. | ||
353 | * | ||
354 | * @param action | ||
355 | * the action to be run when reaching the steady-state. | ||
356 | * | ||
357 | * To be called from any user thread. | ||
358 | */ | ||
359 | public void waitForReteTermination(Runnable action) { | ||
360 | if (threads > 0) { | ||
361 | synchronized (globalTerminationCriteria) { | ||
362 | while (!globalTerminationCriteria.isEmpty()) { | ||
363 | try { | ||
364 | globalTerminationCriteria.wait(); | ||
365 | } catch (InterruptedException e) { | ||
366 | |||
367 | } | ||
368 | } | ||
369 | action.run(); | ||
370 | } | ||
371 | } else { | ||
372 | headContainer.deliverMessagesSingleThreaded(); | ||
373 | action.run(); | ||
374 | } | ||
375 | |||
376 | } | ||
377 | |||
378 | /** | ||
379 | * @return an unmodifiable set of known recipe traces | ||
380 | */ | ||
381 | public Set<RecipeTraceInfo> getRecipeTraces() { | ||
382 | return Collections.unmodifiableSet(recipeTraces); | ||
383 | } | ||
384 | |||
385 | /** | ||
386 | * @return an unmodifiable list of containers | ||
387 | */ | ||
388 | public List<ReteContainer> getContainers() { | ||
389 | return Collections.unmodifiableList(containers); | ||
390 | } | ||
391 | |||
392 | public Lock getStructuralChangeLock() { | ||
393 | return structuralChangeLock; | ||
394 | } | ||
395 | |||
396 | public NodeFactory getNodeFactory() { | ||
397 | return nodeFactory; | ||
398 | } | ||
399 | |||
400 | public InputConnector getInputConnector() { | ||
401 | return inputConnector; | ||
402 | } | ||
403 | |||
404 | public ReteEngine getEngine() { | ||
405 | return engine; | ||
406 | } | ||
407 | |||
408 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java new file mode 100644 index 00000000..c6ba34c4 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java | |||
@@ -0,0 +1,30 @@ | |||
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.rete.network; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
12 | |||
13 | /** | ||
14 | * {@link Node}s implementing this interface are sensitive to changes in the dependency graph maintained by the | ||
15 | * {@link CommunicationTracker}. The {@link CommunicationTracker} notifies these nodes whenever the SCC of this node is | ||
16 | * affected by changes to the dependency graph. Depending on whether this node is contained in a recursive group or not, | ||
17 | * it may behave differently, and the {@link NetworkStructureChangeSensitiveNode#networkStructureChanged()} method can | ||
18 | * be used to perform changes in behavior. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * @since 2.3 | ||
22 | */ | ||
23 | public interface NetworkStructureChangeSensitiveNode extends Node { | ||
24 | |||
25 | /** | ||
26 | * At the time of the invocation, the dependency graph has already been updated. | ||
27 | */ | ||
28 | public void networkStructureChanged(); | ||
29 | |||
30 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java new file mode 100644 index 00000000..e8ab615a --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java | |||
@@ -0,0 +1,62 @@ | |||
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.rete.network; | ||
11 | |||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
15 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
16 | |||
17 | /** | ||
18 | * A node of a rete network, should be uniquely identified by network and nodeId. NodeId can be requested by registering | ||
19 | * at the Network on construction. | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | */ | ||
23 | public interface Node { | ||
24 | /** | ||
25 | * @return the network this node belongs to. | ||
26 | */ | ||
27 | ReteContainer getContainer(); | ||
28 | |||
29 | /** | ||
30 | * @return the identifier unique to this node within the network. | ||
31 | */ | ||
32 | long getNodeId(); | ||
33 | |||
34 | /** | ||
35 | * Assigns a descriptive tag to the node | ||
36 | */ | ||
37 | void setTag(Object tag); | ||
38 | |||
39 | /** | ||
40 | * @return the tag of the node | ||
41 | */ | ||
42 | Object getTag(); | ||
43 | |||
44 | /** | ||
45 | * @return unmodifiable view of the list of traceability infos assigned to this node | ||
46 | */ | ||
47 | Set<TraceInfo> getTraceInfos(); | ||
48 | |||
49 | /** | ||
50 | * assigns new traceability info to this node | ||
51 | */ | ||
52 | void assignTraceInfo(TraceInfo traceInfo); | ||
53 | /** | ||
54 | * accepts traceability info propagated to this node | ||
55 | */ | ||
56 | void acceptPropagatedTraceInfo(TraceInfo traceInfo); | ||
57 | |||
58 | default CommunicationTracker getCommunicationTracker() { | ||
59 | return getContainer().getCommunicationTracker(); | ||
60 | } | ||
61 | |||
62 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java new file mode 100644 index 00000000..3e4ea4e0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java | |||
@@ -0,0 +1,376 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | package tools.refinery.viatra.runtime.rete.network; | ||
11 | |||
12 | import org.apache.log4j.Logger; | ||
13 | import org.eclipse.emf.common.util.EMap; | ||
14 | import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeElectionAlgorithm; | ||
15 | import tools.refinery.viatra.runtime.rete.itc.alg.representative.StronglyConnectedComponentAlgorithm; | ||
16 | import tools.refinery.viatra.runtime.rete.itc.alg.representative.WeaklyConnectedComponentAlgorithm; | ||
17 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; | ||
20 | import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
21 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
23 | import tools.refinery.viatra.runtime.rete.aggregation.ColumnAggregatorNode; | ||
24 | import tools.refinery.viatra.runtime.rete.aggregation.CountNode; | ||
25 | import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode; | ||
26 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode; | ||
27 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode; | ||
28 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlyParallelTimelyColumnAggregatorNode; | ||
29 | import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlySequentialTimelyColumnAggregatorNode; | ||
30 | import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode; | ||
31 | import tools.refinery.viatra.runtime.rete.boundary.ExternalInputStatelessFilterNode; | ||
32 | import tools.refinery.viatra.runtime.rete.eval.EvaluatorCore; | ||
33 | import tools.refinery.viatra.runtime.rete.eval.MemorylessEvaluatorNode; | ||
34 | import tools.refinery.viatra.runtime.rete.eval.OutputCachingEvaluatorNode; | ||
35 | import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode; | ||
36 | import tools.refinery.viatra.runtime.rete.index.ExistenceNode; | ||
37 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
38 | import tools.refinery.viatra.runtime.rete.index.JoinNode; | ||
39 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; | ||
40 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture; | ||
41 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; | ||
42 | import tools.refinery.viatra.runtime.rete.misc.ConstantNode; | ||
43 | import tools.refinery.viatra.runtime.rete.recipes.*; | ||
44 | import tools.refinery.viatra.runtime.rete.single.*; | ||
45 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
46 | |||
47 | import java.util.HashMap; | ||
48 | import java.util.List; | ||
49 | import java.util.Map; | ||
50 | |||
51 | /** | ||
52 | * Factory for instantiating Rete nodes. The created nodes are not connected to the network yet. | ||
53 | * | ||
54 | * @author Bergmann Gabor | ||
55 | * | ||
56 | */ | ||
57 | class NodeFactory { | ||
58 | Logger logger; | ||
59 | |||
60 | public NodeFactory(Logger logger) { | ||
61 | super(); | ||
62 | this.logger = logger; | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * PRE: parent node must already be created | ||
67 | */ | ||
68 | public Indexer createIndexer(ReteContainer reteContainer, IndexerRecipe recipe, Supplier parentNode, | ||
69 | TraceInfo... traces) { | ||
70 | |||
71 | if (recipe instanceof ProjectionIndexerRecipe) { | ||
72 | return parentNode.constructIndex(toMask(recipe.getMask()), traces); | ||
73 | // already traced | ||
74 | } else if (recipe instanceof AggregatorIndexerRecipe) { | ||
75 | int indexOfAggregateResult = recipe.getParent().getArity(); | ||
76 | int resultPosition = recipe.getMask().getSourceIndices().lastIndexOf(indexOfAggregateResult); | ||
77 | |||
78 | IAggregatorNode aggregatorNode = (IAggregatorNode) parentNode; | ||
79 | final Indexer result = (resultPosition == -1) ? aggregatorNode.getAggregatorOuterIndexer() | ||
80 | : aggregatorNode.getAggregatorOuterIdentityIndexer(resultPosition); | ||
81 | |||
82 | for (TraceInfo traceInfo : traces) | ||
83 | result.assignTraceInfo(traceInfo); | ||
84 | return result; | ||
85 | } else | ||
86 | throw new IllegalArgumentException("Unkown Indexer recipe: " + recipe); | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * PRE: recipe is not an indexer recipe. | ||
91 | */ | ||
92 | public Supplier createNode(ReteContainer reteContainer, ReteNodeRecipe recipe, TraceInfo... traces) { | ||
93 | if (recipe instanceof IndexerRecipe) | ||
94 | throw new IllegalArgumentException("Indexers are not created by NodeFactory: " + recipe); | ||
95 | |||
96 | Supplier result = instantiateNodeDispatch(reteContainer, recipe); | ||
97 | for (TraceInfo traceInfo : traces) | ||
98 | result.assignTraceInfo(traceInfo); | ||
99 | return result; | ||
100 | } | ||
101 | |||
102 | private Supplier instantiateNodeDispatch(ReteContainer reteContainer, ReteNodeRecipe recipe) { | ||
103 | |||
104 | // Parentless | ||
105 | |||
106 | if (recipe instanceof ConstantRecipe) | ||
107 | return instantiateNode(reteContainer, (ConstantRecipe) recipe); | ||
108 | if (recipe instanceof InputRecipe) | ||
109 | return instantiateNode(reteContainer, (InputRecipe) recipe); | ||
110 | |||
111 | // SingleParentNodeRecipe | ||
112 | |||
113 | // if (recipe instanceof ProjectionIndexer) | ||
114 | // return instantiateNode((ProjectionIndexer)recipe); | ||
115 | if (recipe instanceof InputFilterRecipe) | ||
116 | return instantiateNode(reteContainer, (InputFilterRecipe) recipe); | ||
117 | if (recipe instanceof InequalityFilterRecipe) | ||
118 | return instantiateNode(reteContainer, (InequalityFilterRecipe) recipe); | ||
119 | if (recipe instanceof EqualityFilterRecipe) | ||
120 | return instantiateNode(reteContainer, (EqualityFilterRecipe) recipe); | ||
121 | if (recipe instanceof TransparentRecipe) | ||
122 | return instantiateNode(reteContainer, (TransparentRecipe) recipe); | ||
123 | if (recipe instanceof TrimmerRecipe) | ||
124 | return instantiateNode(reteContainer, (TrimmerRecipe) recipe); | ||
125 | if (recipe instanceof TransitiveClosureRecipe) | ||
126 | return instantiateNode(reteContainer, (TransitiveClosureRecipe) recipe); | ||
127 | if (recipe instanceof RepresentativeElectionRecipe) | ||
128 | return instantiateNode(reteContainer, (RepresentativeElectionRecipe) recipe); | ||
129 | if (recipe instanceof RelationEvaluationRecipe) | ||
130 | return instantiateNode(reteContainer, (RelationEvaluationRecipe) recipe); | ||
131 | if (recipe instanceof ExpressionEnforcerRecipe) | ||
132 | return instantiateNode(reteContainer, (ExpressionEnforcerRecipe) recipe); | ||
133 | if (recipe instanceof CountAggregatorRecipe) | ||
134 | return instantiateNode(reteContainer, (CountAggregatorRecipe) recipe); | ||
135 | if (recipe instanceof SingleColumnAggregatorRecipe) | ||
136 | return instantiateNode(reteContainer, (SingleColumnAggregatorRecipe) recipe); | ||
137 | if (recipe instanceof DiscriminatorDispatcherRecipe) | ||
138 | return instantiateNode(reteContainer, (DiscriminatorDispatcherRecipe) recipe); | ||
139 | if (recipe instanceof DiscriminatorBucketRecipe) | ||
140 | return instantiateNode(reteContainer, (DiscriminatorBucketRecipe) recipe); | ||
141 | |||
142 | // MultiParentNodeRecipe | ||
143 | if (recipe instanceof UniquenessEnforcerRecipe) | ||
144 | return instantiateNode(reteContainer, (UniquenessEnforcerRecipe) recipe); | ||
145 | if (recipe instanceof ProductionRecipe) | ||
146 | return instantiateNode(reteContainer, (ProductionRecipe) recipe); | ||
147 | |||
148 | // BetaNodeRecipe | ||
149 | if (recipe instanceof JoinRecipe) | ||
150 | return instantiateNode(reteContainer, (JoinRecipe) recipe); | ||
151 | if (recipe instanceof SemiJoinRecipe) | ||
152 | return instantiateNode(reteContainer, (SemiJoinRecipe) recipe); | ||
153 | if (recipe instanceof AntiJoinRecipe) | ||
154 | return instantiateNode(reteContainer, (AntiJoinRecipe) recipe); | ||
155 | |||
156 | // ... else | ||
157 | throw new IllegalArgumentException("Unsupported recipe type: " + recipe); | ||
158 | } | ||
159 | |||
160 | // INSTANTIATION for recipe types | ||
161 | |||
162 | private Supplier instantiateNode(ReteContainer reteContainer, InputRecipe recipe) { | ||
163 | return new ExternalInputEnumeratorNode(reteContainer); | ||
164 | } | ||
165 | |||
166 | private Supplier instantiateNode(ReteContainer reteContainer, InputFilterRecipe recipe) { | ||
167 | return new ExternalInputStatelessFilterNode(reteContainer, toMaskOrNull(recipe.getMask())); | ||
168 | } | ||
169 | |||
170 | private Supplier instantiateNode(ReteContainer reteContainer, CountAggregatorRecipe recipe) { | ||
171 | return new CountNode(reteContainer); | ||
172 | } | ||
173 | |||
174 | private Supplier instantiateNode(ReteContainer reteContainer, TransparentRecipe recipe) { | ||
175 | return new TransparentNode(reteContainer); | ||
176 | } | ||
177 | |||
178 | private Supplier instantiateNode(ReteContainer reteContainer, ExpressionEnforcerRecipe recipe) { | ||
179 | final IExpressionEvaluator evaluator = toIExpressionEvaluator(recipe.getExpression()); | ||
180 | final Map<String, Integer> posMapping = toStringIndexMap(recipe.getMappedIndices()); | ||
181 | final int sourceTupleWidth = recipe.getParent().getArity(); | ||
182 | EvaluatorCore core = null; | ||
183 | if (recipe instanceof CheckRecipe) { | ||
184 | core = new EvaluatorCore.PredicateEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth); | ||
185 | } else if (recipe instanceof EvalRecipe) { | ||
186 | final boolean isUnwinding = ((EvalRecipe) recipe).isUnwinding(); | ||
187 | core = new EvaluatorCore.FunctionEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth, isUnwinding); | ||
188 | } else { | ||
189 | throw new IllegalArgumentException("Unhandled expression enforcer recipe: " + recipe.getClass() + "!"); | ||
190 | } | ||
191 | if (recipe.isCacheOutput()) { | ||
192 | return new OutputCachingEvaluatorNode(reteContainer, core); | ||
193 | } else { | ||
194 | return new MemorylessEvaluatorNode(reteContainer, core); | ||
195 | } | ||
196 | } | ||
197 | |||
198 | @SuppressWarnings({ "rawtypes", "unchecked" }) | ||
199 | private Supplier instantiateNode(ReteContainer reteContainer, SingleColumnAggregatorRecipe recipe) { | ||
200 | final IMultisetAggregationOperator operator = recipe.getMultisetAggregationOperator(); | ||
201 | TupleMask coreMask = null; | ||
202 | if (recipe.getOptionalMonotonicityInfo() != null) { | ||
203 | coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask()); | ||
204 | } else { | ||
205 | coreMask = toMask(recipe.getGroupByMask()); | ||
206 | } | ||
207 | |||
208 | if (reteContainer.isTimelyEvaluation()) { | ||
209 | final TimelyConfiguration timelyConfiguration = reteContainer.getTimelyConfiguration(); | ||
210 | final AggregatorArchitecture aggregatorArchitecture = timelyConfiguration.getAggregatorArchitecture(); | ||
211 | final TimelineRepresentation timelineRepresentation = timelyConfiguration.getTimelineRepresentation(); | ||
212 | |||
213 | TupleMask posetMask = null; | ||
214 | |||
215 | if (recipe.getOptionalMonotonicityInfo() != null) { | ||
216 | posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); | ||
217 | } else { | ||
218 | final int aggregatedColumn = recipe.getAggregableIndex(); | ||
219 | posetMask = TupleMask.selectSingle(aggregatedColumn, coreMask.sourceWidth); | ||
220 | } | ||
221 | |||
222 | if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY | ||
223 | && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) { | ||
224 | return new FirstOnlySequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); | ||
225 | } else if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY | ||
226 | && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) { | ||
227 | return new FirstOnlyParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); | ||
228 | } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL | ||
229 | && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) { | ||
230 | return new FaithfulSequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); | ||
231 | } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL | ||
232 | && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) { | ||
233 | return new FaithfulParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); | ||
234 | } else { | ||
235 | throw new IllegalArgumentException("Unsupported timely configuration!"); | ||
236 | } | ||
237 | } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) { | ||
238 | final TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); | ||
239 | final IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo() | ||
240 | .getPosetComparator(); | ||
241 | return new ColumnAggregatorNode(reteContainer, operator, recipe.isDeleteRederiveEvaluation(), coreMask, | ||
242 | posetMask, posetComparator); | ||
243 | } else { | ||
244 | final int aggregatedColumn = recipe.getAggregableIndex(); | ||
245 | return new ColumnAggregatorNode(reteContainer, operator, coreMask, aggregatedColumn); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | private Supplier instantiateNode(ReteContainer reteContainer, TransitiveClosureRecipe recipe) { | ||
250 | return new TransitiveClosureNode(reteContainer); | ||
251 | } | ||
252 | |||
253 | private Supplier instantiateNode(ReteContainer reteContainer, RepresentativeElectionRecipe recipe) { | ||
254 | RepresentativeElectionAlgorithm.Factory algorithmFactory = switch (recipe.getConnectivity()) { | ||
255 | case STRONG -> StronglyConnectedComponentAlgorithm::new; | ||
256 | case WEAK -> WeaklyConnectedComponentAlgorithm::new; | ||
257 | }; | ||
258 | return new RepresentativeElectionNode(reteContainer, algorithmFactory); | ||
259 | } | ||
260 | |||
261 | private Supplier instantiateNode(ReteContainer reteContainer, RelationEvaluationRecipe recipe) { | ||
262 | return new RelationEvaluatorNode(reteContainer, toIRelationEvaluator(recipe.getEvaluator())); | ||
263 | } | ||
264 | |||
265 | private Supplier instantiateNode(ReteContainer reteContainer, ProductionRecipe recipe) { | ||
266 | if (reteContainer.isTimelyEvaluation()) { | ||
267 | return new TimelyProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices())); | ||
268 | } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) { | ||
269 | TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask()); | ||
270 | TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); | ||
271 | IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo() | ||
272 | .getPosetComparator(); | ||
273 | return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()), | ||
274 | recipe.isDeleteRederiveEvaluation(), coreMask, posetMask, posetComparator); | ||
275 | } else { | ||
276 | return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()), | ||
277 | recipe.isDeleteRederiveEvaluation()); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | private Supplier instantiateNode(ReteContainer reteContainer, UniquenessEnforcerRecipe recipe) { | ||
282 | if (reteContainer.isTimelyEvaluation()) { | ||
283 | return new TimelyUniquenessEnforcerNode(reteContainer, recipe.getArity()); | ||
284 | } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) { | ||
285 | TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask()); | ||
286 | TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); | ||
287 | IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo() | ||
288 | .getPosetComparator(); | ||
289 | return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation(), | ||
290 | coreMask, posetMask, posetComparator); | ||
291 | } else { | ||
292 | return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation()); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | private Supplier instantiateNode(ReteContainer reteContainer, ConstantRecipe recipe) { | ||
297 | final List<Object> constantValues = recipe.getConstantValues(); | ||
298 | final Object[] constantArray = constantValues.toArray(new Object[constantValues.size()]); | ||
299 | return new ConstantNode(reteContainer, Tuples.flatTupleOf(constantArray)); | ||
300 | } | ||
301 | |||
302 | private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorBucketRecipe recipe) { | ||
303 | return new DiscriminatorBucketNode(reteContainer, recipe.getBucketKey()); | ||
304 | } | ||
305 | |||
306 | private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorDispatcherRecipe recipe) { | ||
307 | return new DiscriminatorDispatcherNode(reteContainer, recipe.getDiscriminationColumnIndex()); | ||
308 | } | ||
309 | |||
310 | private Supplier instantiateNode(ReteContainer reteContainer, TrimmerRecipe recipe) { | ||
311 | return new TrimmerNode(reteContainer, toMask(recipe.getMask())); | ||
312 | } | ||
313 | |||
314 | private Supplier instantiateNode(ReteContainer reteContainer, InequalityFilterRecipe recipe) { | ||
315 | Tunnel result = new InequalityFilterNode(reteContainer, recipe.getSubject(), | ||
316 | TupleMask.fromSelectedIndices(recipe.getParent().getArity(), recipe.getInequals())); | ||
317 | return result; | ||
318 | } | ||
319 | |||
320 | private Supplier instantiateNode(ReteContainer reteContainer, EqualityFilterRecipe recipe) { | ||
321 | final int[] equalIndices = TupleMask.integersToIntArray(recipe.getIndices()); | ||
322 | return new EqualityFilterNode(reteContainer, equalIndices); | ||
323 | } | ||
324 | |||
325 | private Supplier instantiateNode(ReteContainer reteContainer, AntiJoinRecipe recipe) { | ||
326 | return new ExistenceNode(reteContainer, true); | ||
327 | } | ||
328 | |||
329 | private Supplier instantiateNode(ReteContainer reteContainer, SemiJoinRecipe recipe) { | ||
330 | return new ExistenceNode(reteContainer, false); | ||
331 | } | ||
332 | |||
333 | private Supplier instantiateNode(ReteContainer reteContainer, JoinRecipe recipe) { | ||
334 | return new JoinNode(reteContainer, toMask(recipe.getRightParentComplementaryMask())); | ||
335 | } | ||
336 | |||
337 | // HELPERS | ||
338 | |||
339 | private IExpressionEvaluator toIExpressionEvaluator(ExpressionDefinition expressionDefinition) { | ||
340 | final Object evaluator = expressionDefinition.getEvaluator(); | ||
341 | if (evaluator instanceof IExpressionEvaluator) { | ||
342 | return (IExpressionEvaluator) evaluator; | ||
343 | } | ||
344 | throw new IllegalArgumentException("No runtime support for expression evaluator: " + evaluator); | ||
345 | } | ||
346 | |||
347 | private IRelationEvaluator toIRelationEvaluator(ExpressionDefinition expressionDefinition) { | ||
348 | final Object evaluator = expressionDefinition.getEvaluator(); | ||
349 | if (evaluator instanceof IRelationEvaluator) { | ||
350 | return (IRelationEvaluator) evaluator; | ||
351 | } | ||
352 | throw new IllegalArgumentException("No runtime support for relation evaluator: " + evaluator); | ||
353 | } | ||
354 | |||
355 | private Map<String, Integer> toStringIndexMap(final EMap<String, Integer> mappedIndices) { | ||
356 | final HashMap<String, Integer> result = new HashMap<String, Integer>(); | ||
357 | for (java.util.Map.Entry<String, Integer> entry : mappedIndices) { | ||
358 | result.put(entry.getKey(), entry.getValue()); | ||
359 | } | ||
360 | return result; | ||
361 | } | ||
362 | |||
363 | /** Mask can be null */ | ||
364 | private TupleMask toMaskOrNull(Mask mask) { | ||
365 | if (mask == null) | ||
366 | return null; | ||
367 | else | ||
368 | return toMask(mask); | ||
369 | } | ||
370 | |||
371 | /** Mask is non-null. */ | ||
372 | private TupleMask toMask(Mask mask) { | ||
373 | return TupleMask.fromSelectedIndices(mask.getSourceArity(), mask.getSourceIndices()); | ||
374 | } | ||
375 | |||
376 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java new file mode 100644 index 00000000..9121fc44 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java | |||
@@ -0,0 +1,346 @@ | |||
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.rete.network; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
17 | import tools.refinery.viatra.runtime.rete.boundary.InputConnector; | ||
18 | import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper; | ||
19 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
20 | import tools.refinery.viatra.runtime.rete.index.OnetimeIndexer; | ||
21 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand; | ||
23 | import tools.refinery.viatra.runtime.rete.recipes.*; | ||
24 | import tools.refinery.viatra.runtime.rete.recipes.helper.RecipeRecognizer; | ||
25 | import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; | ||
26 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
27 | import tools.refinery.viatra.runtime.rete.remote.RemoteReceiver; | ||
28 | import tools.refinery.viatra.runtime.rete.remote.RemoteSupplier; | ||
29 | import tools.refinery.viatra.runtime.rete.traceability.ActiveNodeConflictTrace; | ||
30 | import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; | ||
31 | import tools.refinery.viatra.runtime.rete.traceability.UserRequestTrace; | ||
32 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
33 | |||
34 | import java.util.Map; | ||
35 | import java.util.Set; | ||
36 | |||
37 | /** | ||
38 | * Stores the internal parts of a rete network. Nodes are stored according to type and parameters. | ||
39 | * | ||
40 | * @author Gabor Bergmann | ||
41 | */ | ||
42 | public class NodeProvisioner { | ||
43 | |||
44 | // boolean activeStorage = true; | ||
45 | |||
46 | ReteContainer reteContainer; | ||
47 | NodeFactory nodeFactory; | ||
48 | ConnectionFactory connectionFactory; | ||
49 | InputConnector inputConnector; | ||
50 | IQueryRuntimeContext runtimeContext; | ||
51 | |||
52 | // TODO as recipe? | ||
53 | Map<Supplier, RemoteReceiver> remoteReceivers = CollectionsFactory.createMap(); | ||
54 | Map<Address<? extends Supplier>, RemoteSupplier> remoteSuppliers = CollectionsFactory.createMap(); | ||
55 | |||
56 | private RecipeRecognizer recognizer; | ||
57 | |||
58 | /** | ||
59 | * PRE: NodeFactory, ConnectionFactory must exist | ||
60 | * | ||
61 | * @param reteContainer | ||
62 | * the ReteNet whose interior is to be mapped. | ||
63 | */ | ||
64 | public NodeProvisioner(ReteContainer reteContainer) { | ||
65 | super(); | ||
66 | this.reteContainer = reteContainer; | ||
67 | this.nodeFactory = reteContainer.getNodeFactory(); | ||
68 | this.connectionFactory = reteContainer.getConnectionFactory(); | ||
69 | this.inputConnector = reteContainer.getInputConnectionFactory(); | ||
70 | runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext(); | ||
71 | recognizer = new RecipeRecognizer(runtimeContext); | ||
72 | } | ||
73 | |||
74 | public synchronized Address<? extends Node> getOrCreateNodeByRecipe(RecipeTraceInfo recipeTrace) { | ||
75 | ReteNodeRecipe recipe = recipeTrace.getRecipe(); | ||
76 | Address<? extends Node> result = getNodesByRecipe().get(recipe); | ||
77 | if (result != null) { | ||
78 | // NODE ALREADY CONSTRUCTED FOR RECIPE, only needs to add trace | ||
79 | if (getRecipeTraces().add(recipeTrace)) | ||
80 | result.getNodeCache().assignTraceInfo(recipeTrace); | ||
81 | } else { | ||
82 | // No node for this recipe object - but equivalent recipes still | ||
83 | // reusable | ||
84 | ReteNodeRecipe canonicalRecipe = recognizer.canonicalizeRecipe(recipe); | ||
85 | if (canonicalRecipe != recipe) { | ||
86 | // FOUND EQUIVALENT RECIPE | ||
87 | result = getNodesByRecipe().get(canonicalRecipe); | ||
88 | if (result != null) { | ||
89 | // NODE ALREADY CONSTRUCTED FOR EQUIVALENT RECIPE | ||
90 | recipeTrace.shadowWithEquivalentRecipe(canonicalRecipe); | ||
91 | getNodesByRecipe().put(recipe, result); | ||
92 | if (getRecipeTraces().add(recipeTrace)) | ||
93 | result.getNodeCache().assignTraceInfo(recipeTrace); | ||
94 | // Bug 491922: ensure that recipe shadowing propagates to | ||
95 | // parent traces | ||
96 | // note that if equivalentRecipes() becomes more | ||
97 | // sophisticated | ||
98 | // and considers recipes with different parents, this might | ||
99 | // have to be changed | ||
100 | ensureParents(recipeTrace); | ||
101 | } else { | ||
102 | // CONSTRUCTION IN PROGRESS FOR EQUIVALENT RECIPE | ||
103 | if (recipe instanceof IndexerRecipe) { | ||
104 | // this is allowed for indexers; | ||
105 | // go on with the construction, as the same indexer node | ||
106 | // will be obtained anyways | ||
107 | } else { | ||
108 | throw new IllegalStateException( | ||
109 | "This should not happen: " + "non-indexer nodes are are supposed to be constructed " | ||
110 | + "as soon as they are designated as canonical recipes"); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | if (result == null) { | ||
115 | // MUST INSTANTIATE NEW NODE FOR RECIPE | ||
116 | final Node freshNode = instantiateNodeForRecipe(recipeTrace, recipe); | ||
117 | result = reteContainer.makeAddress(freshNode); | ||
118 | } | ||
119 | } | ||
120 | return result; | ||
121 | } | ||
122 | |||
123 | private Set<RecipeTraceInfo> getRecipeTraces() { | ||
124 | return reteContainer.network.recipeTraces; | ||
125 | } | ||
126 | |||
127 | private Node instantiateNodeForRecipe(RecipeTraceInfo recipeTrace, final ReteNodeRecipe recipe) { | ||
128 | this.getRecipeTraces().add(recipeTrace); | ||
129 | if (recipe instanceof IndexerRecipe) { | ||
130 | |||
131 | // INSTANTIATE AND HOOK UP | ||
132 | // (cannot delay hooking up, because parent determines indexer | ||
133 | // implementation) | ||
134 | ensureParents(recipeTrace); | ||
135 | final ReteNodeRecipe parentRecipe = recipeTrace.getParentRecipeTraces().iterator().next().getRecipe(); | ||
136 | final Indexer result = nodeFactory.createIndexer(reteContainer, (IndexerRecipe) recipe, | ||
137 | asSupplier( | ||
138 | (Address<? extends Supplier>) reteContainer.network.getExistingNodeByRecipe(parentRecipe)), | ||
139 | recipeTrace); | ||
140 | |||
141 | // REMEMBER | ||
142 | if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) { | ||
143 | getNodesByRecipe().put(recipe, reteContainer.makeAddress(result)); | ||
144 | } | ||
145 | |||
146 | return result; | ||
147 | } else { | ||
148 | |||
149 | // INSTANTIATE | ||
150 | Node result = nodeFactory.createNode(reteContainer, recipe, recipeTrace); | ||
151 | |||
152 | // REMEMBER | ||
153 | if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) { | ||
154 | getNodesByRecipe().put(recipe, reteContainer.makeAddress(result)); | ||
155 | } | ||
156 | |||
157 | // HOOK UP | ||
158 | // (recursion-tolerant due to this delayed order of initialization) | ||
159 | if (recipe instanceof InputRecipe) { | ||
160 | inputConnector.connectInput((InputRecipe) recipe, result); | ||
161 | } else { | ||
162 | if (recipe instanceof InputFilterRecipe) | ||
163 | inputConnector.connectInputFilter((InputFilterRecipe) recipe, result); | ||
164 | ensureParents(recipeTrace); | ||
165 | connectionFactory.connectToParents(recipeTrace, result); | ||
166 | } | ||
167 | return result; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | private Map<ReteNodeRecipe, Address<? extends Node>> getNodesByRecipe() { | ||
172 | return reteContainer.network.nodesByRecipe; | ||
173 | } | ||
174 | |||
175 | private void ensureParents(RecipeTraceInfo recipeTrace) { | ||
176 | for (RecipeTraceInfo parentTrace : recipeTrace.getParentRecipeTraces()) { | ||
177 | getOrCreateNodeByRecipe(parentTrace); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | //// Remoting - TODO eliminate? | ||
182 | |||
183 | synchronized RemoteReceiver accessRemoteReceiver(Address<? extends Supplier> address) { | ||
184 | throw new UnsupportedOperationException("Multi-container Rete not supported yet"); | ||
185 | // if (!reteContainer.isLocal(address)) | ||
186 | // return | ||
187 | // address.getContainer().getProvisioner().accessRemoteReceiver(address); | ||
188 | // Supplier localSupplier = reteContainer.resolveLocal(address); | ||
189 | // RemoteReceiver result = remoteReceivers.get(localSupplier); | ||
190 | // if (result == null) { | ||
191 | // result = new RemoteReceiver(reteContainer); | ||
192 | // reteContainer.connect(localSupplier, result); // stateless node, no | ||
193 | // // synch required | ||
194 | // | ||
195 | // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) | ||
196 | // remoteReceivers.put(localSupplier, result); | ||
197 | // } | ||
198 | // return result; | ||
199 | } | ||
200 | |||
201 | /** | ||
202 | * @pre: address is NOT local | ||
203 | */ | ||
204 | synchronized RemoteSupplier accessRemoteSupplier(Address<? extends Supplier> address) { | ||
205 | throw new UnsupportedOperationException("Multi-container Rete not supported yet"); | ||
206 | // RemoteSupplier result = remoteSuppliers.get(address); | ||
207 | // if (result == null) { | ||
208 | // result = new RemoteSupplier(reteContainer, | ||
209 | // address.getContainer().getProvisioner() | ||
210 | // .accessRemoteReceiver(address)); | ||
211 | // // network.connectAndSynchronize(supplier, result); | ||
212 | // | ||
213 | // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) | ||
214 | // remoteSuppliers.put(address, result); | ||
215 | // } | ||
216 | // return result; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * The powerful method for accessing any (supplier) Address as a local supplier. | ||
221 | */ | ||
222 | public Supplier asSupplier(Address<? extends Supplier> address) { | ||
223 | if (!reteContainer.isLocal(address)) | ||
224 | return accessRemoteSupplier(address); | ||
225 | else | ||
226 | return reteContainer.resolveLocal(address); | ||
227 | } | ||
228 | |||
229 | /** the composite key tuple is formed as (RecipeTraceInfo, TupleMask) */ | ||
230 | private Map<Tuple, UserRequestTrace> projectionIndexerUserRequests = CollectionsFactory.createMap(); | ||
231 | |||
232 | // local version | ||
233 | // TODO remove? | ||
234 | public synchronized ProjectionIndexer accessProjectionIndexer(RecipeTraceInfo productionTrace, TupleMask mask) { | ||
235 | Tuple tableKey = Tuples.staticArityFlatTupleOf(productionTrace, mask); | ||
236 | UserRequestTrace indexerTrace = projectionIndexerUserRequests.computeIfAbsent(tableKey, k -> { | ||
237 | final ProjectionIndexerRecipe projectionIndexerRecipe = projectionIndexerRecipe( | ||
238 | productionTrace, mask); | ||
239 | return new UserRequestTrace(projectionIndexerRecipe, productionTrace); | ||
240 | }); | ||
241 | final Address<? extends Node> address = getOrCreateNodeByRecipe(indexerTrace); | ||
242 | return (ProjectionIndexer) reteContainer.resolveLocal(address); | ||
243 | } | ||
244 | |||
245 | // local version | ||
246 | public synchronized ProjectionIndexer accessProjectionIndexerOnetime(RecipeTraceInfo supplierTrace, | ||
247 | TupleMask mask) { | ||
248 | if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) | ||
249 | return accessProjectionIndexer(supplierTrace, mask); | ||
250 | |||
251 | final Address<? extends Node> supplierAddress = getOrCreateNodeByRecipe(supplierTrace); | ||
252 | Supplier supplier = (Supplier) reteContainer.resolveLocal(supplierAddress); | ||
253 | |||
254 | OnetimeIndexer result = new OnetimeIndexer(reteContainer, mask); | ||
255 | reteContainer.getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, result, reteContainer)); | ||
256 | |||
257 | return result; | ||
258 | } | ||
259 | |||
260 | // local, read-only version | ||
261 | public synchronized ProjectionIndexer peekProjectionIndexer(RecipeTraceInfo supplierTrace, TupleMask mask) { | ||
262 | final Address<? extends Node> address = getNodesByRecipe().get(projectionIndexerRecipe(supplierTrace, mask)); | ||
263 | return address == null ? null : (ProjectionIndexer) reteContainer.resolveLocal(address); | ||
264 | } | ||
265 | |||
266 | private ProjectionIndexerRecipe projectionIndexerRecipe( | ||
267 | RecipeTraceInfo parentTrace, TupleMask mask) { | ||
268 | final ReteNodeRecipe parentRecipe = parentTrace.getRecipe(); | ||
269 | Tuple tableKey = Tuples.staticArityFlatTupleOf(parentRecipe, mask); | ||
270 | ProjectionIndexerRecipe projectionIndexerRecipe = resultSeedRecipes.computeIfAbsent(tableKey, k -> | ||
271 | RecipesHelper.projectionIndexerRecipe(parentRecipe, CompilerHelper.toRecipeMask(mask)) | ||
272 | ); | ||
273 | return projectionIndexerRecipe; | ||
274 | } | ||
275 | |||
276 | /** the composite key tuple is formed as (ReteNodeRecipe, TupleMask) */ | ||
277 | private Map<Tuple, ProjectionIndexerRecipe> resultSeedRecipes = CollectionsFactory.createMap(); | ||
278 | |||
279 | // public synchronized Address<? extends Supplier> | ||
280 | // accessValueBinderFilterNode( | ||
281 | // Address<? extends Supplier> supplierAddress, int bindingIndex, Object | ||
282 | // bindingValue) { | ||
283 | // Supplier supplier = asSupplier(supplierAddress); | ||
284 | // Object[] paramsArray = { supplier.getNodeId(), bindingIndex, bindingValue | ||
285 | // }; | ||
286 | // Tuple params = new FlatTuple(paramsArray); | ||
287 | // ValueBinderFilterNode result = valueBinderFilters.get(params); | ||
288 | // if (result == null) { | ||
289 | // result = new ValueBinderFilterNode(reteContainer, bindingIndex, | ||
290 | // bindingValue); | ||
291 | // reteContainer.connect(supplier, result); // stateless node, no synch | ||
292 | // // required | ||
293 | // | ||
294 | // if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) | ||
295 | // valueBinderFilters.put(params, result); | ||
296 | // } | ||
297 | // return reteContainer.makeAddress(result); | ||
298 | // } | ||
299 | |||
300 | /** | ||
301 | * Returns a copy of the given indexer that is an active node by itself (created if does not exist). (Convention: | ||
302 | * attached with same mask to a transparent node that is attached to parent node.) Node is created if it does not | ||
303 | * exist yet. | ||
304 | * | ||
305 | * @return an identical but active indexer | ||
306 | */ | ||
307 | // TODO rethink traceability | ||
308 | RecipeTraceInfo accessActiveIndexer(RecipeTraceInfo inactiveIndexerRecipeTrace) { | ||
309 | final RecipeTraceInfo parentRecipeTrace = inactiveIndexerRecipeTrace.getParentRecipeTraces().iterator().next(); | ||
310 | final ProjectionIndexerRecipe inactiveIndexerRecipe = (ProjectionIndexerRecipe) inactiveIndexerRecipeTrace | ||
311 | .getRecipe(); | ||
312 | |||
313 | final TransparentRecipe transparentRecipe = RecipesFactory.eINSTANCE.createTransparentRecipe(); | ||
314 | transparentRecipe.setParent(parentRecipeTrace.getRecipe()); | ||
315 | final ActiveNodeConflictTrace transparentRecipeTrace = new ActiveNodeConflictTrace(transparentRecipe, | ||
316 | parentRecipeTrace, inactiveIndexerRecipeTrace); | ||
317 | |||
318 | final ProjectionIndexerRecipe activeIndexerRecipe = RecipesFactory.eINSTANCE | ||
319 | .createProjectionIndexerRecipe(); | ||
320 | activeIndexerRecipe.setParent(transparentRecipe); | ||
321 | activeIndexerRecipe.setMask(inactiveIndexerRecipe.getMask()); | ||
322 | final ActiveNodeConflictTrace activeIndexerRecipeTrace = new ActiveNodeConflictTrace(activeIndexerRecipe, | ||
323 | transparentRecipeTrace, inactiveIndexerRecipeTrace); | ||
324 | |||
325 | return activeIndexerRecipeTrace; | ||
326 | } | ||
327 | |||
328 | // /** | ||
329 | // * @param parent | ||
330 | // * @return | ||
331 | // */ | ||
332 | // private TransparentNode accessTransparentNodeInternal(Supplier parent) { | ||
333 | // nodeFactory. | ||
334 | // return null; | ||
335 | // } | ||
336 | |||
337 | // public synchronized void registerSpecializedProjectionIndexer(Node node, | ||
338 | // ProjectionIndexer indexer) { | ||
339 | // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) { | ||
340 | // Object[] paramsArray = { node.getNodeId(), indexer.getMask() }; | ||
341 | // Tuple params = new FlatTuple(paramsArray); | ||
342 | // projectionIndexers.put(params, indexer); | ||
343 | // } | ||
344 | // } | ||
345 | |||
346 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java new file mode 100644 index 00000000..1eaa18e7 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java | |||
@@ -0,0 +1,39 @@ | |||
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.rete.network; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
15 | |||
16 | /** | ||
17 | * @author Tamas Szabo | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public interface PosetAwareReceiver extends Receiver { | ||
21 | |||
22 | public TupleMask getCoreMask(); | ||
23 | |||
24 | public TupleMask getPosetMask(); | ||
25 | |||
26 | public IPosetComparator getPosetComparator(); | ||
27 | |||
28 | /** | ||
29 | * Updates the receiver with a newly found or lost partial matching also providing information | ||
30 | * whether the update is a monotone change or not. | ||
31 | * | ||
32 | * @param direction the direction of the update | ||
33 | * @param update the update tuple | ||
34 | * @param monotone true if the update is monotone, false otherwise | ||
35 | * @since 2.4 | ||
36 | */ | ||
37 | public void updateWithPosetInfo(Direction direction, Tuple update, boolean monotone); | ||
38 | |||
39 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java new file mode 100644 index 00000000..211194c0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java | |||
@@ -0,0 +1,28 @@ | |||
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.rete.network; | ||
11 | |||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | |||
16 | /** | ||
17 | * Interface intended for nodes containing complete matches. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | */ | ||
21 | public interface ProductionNode extends Tunnel, Iterable<Tuple> { | ||
22 | |||
23 | /** | ||
24 | * @return the position mapping of this particular pattern that maps members of the tuple type to their positions | ||
25 | */ | ||
26 | Map<String, Integer> getPosMapping(); | ||
27 | |||
28 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java new file mode 100644 index 00000000..3dc9aad7 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java | |||
@@ -0,0 +1,85 @@ | |||
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.rete.network; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
18 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
19 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
20 | |||
21 | /** | ||
22 | * ALL METHODS: FOR INTERNAL USE ONLY; ONLY INVOKE FROM {@link ReteContainer} | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | * @noimplement This interface is not intended to be implemented by external clients. | ||
26 | */ | ||
27 | public interface Receiver extends Node { | ||
28 | |||
29 | /** | ||
30 | * Updates the receiver with a newly found or lost partial matching. | ||
31 | * | ||
32 | * @since 2.4 | ||
33 | */ | ||
34 | public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp); | ||
35 | |||
36 | /** | ||
37 | * Updates the receiver in batch style with a collection of updates. The input collection consists of pairs in the | ||
38 | * form (t, c) where t is an update tuple and c is the count. The count can also be negative, and it specifies how | ||
39 | * many times the tuple t gets deleted or inserted. The default implementation of this method simply calls | ||
40 | * {@link #update(Direction, Tuple, Timestamp)} individually for all updates. | ||
41 | * | ||
42 | * @since 2.8 | ||
43 | */ | ||
44 | public default void batchUpdate(final Collection<Map.Entry<Tuple, Integer>> updates, final Timestamp timestamp) { | ||
45 | for (final Entry<Tuple, Integer> entry : updates) { | ||
46 | int count = entry.getValue(); | ||
47 | |||
48 | Direction direction; | ||
49 | if (count < 0) { | ||
50 | direction = Direction.DELETE; | ||
51 | count = -count; | ||
52 | } else { | ||
53 | direction = Direction.INSERT; | ||
54 | } | ||
55 | |||
56 | for (int i = 0; i < count; i++) { | ||
57 | update(direction, entry.getKey(), timestamp); | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Returns the {@link Mailbox} of this receiver. | ||
64 | * | ||
65 | * @return the mailbox | ||
66 | * @since 2.0 | ||
67 | */ | ||
68 | public Mailbox getMailbox(); | ||
69 | |||
70 | /** | ||
71 | * appends a parent that will continuously send insert and revoke updates to this supplier | ||
72 | */ | ||
73 | void appendParent(final Supplier supplier); | ||
74 | |||
75 | /** | ||
76 | * removes a parent | ||
77 | */ | ||
78 | void removeParent(final Supplier supplier); | ||
79 | |||
80 | /** | ||
81 | * access active parent | ||
82 | */ | ||
83 | Collection<Supplier> getParents(); | ||
84 | |||
85 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java new file mode 100644 index 00000000..cae78d37 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java | |||
@@ -0,0 +1,34 @@ | |||
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.rete.network; | ||
10 | |||
11 | /** | ||
12 | * A rederivable node can potentially re-derive tuples after the Rete network has finished the delivery of messages. | ||
13 | * | ||
14 | * @author Tamas Szabo | ||
15 | * @since 1.6 | ||
16 | */ | ||
17 | public interface RederivableNode extends Node, IGroupable { | ||
18 | |||
19 | /** | ||
20 | * The method is called by the {@link ReteContainer} to re-derive tuples after the normal messages have been | ||
21 | * delivered and consumed. The re-derivation process may trigger the creation and delivery of further messages | ||
22 | * and further re-derivation rounds. | ||
23 | */ | ||
24 | public void rederiveOne(); | ||
25 | |||
26 | /** | ||
27 | * Returns true if this node actually runs in DRed mode (not necessarily). | ||
28 | * | ||
29 | * @return true if the node is operating in DRed mode | ||
30 | * @since 2.0 | ||
31 | */ | ||
32 | public boolean isInDRedMode(); | ||
33 | |||
34 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java new file mode 100644 index 00000000..09bff29e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java | |||
@@ -0,0 +1,14 @@ | |||
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.rete.network; | ||
7 | |||
8 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
9 | |||
10 | import java.util.Collection; | ||
11 | |||
12 | public interface ReinitializedNode { | ||
13 | void reinitializeWith(Collection<Tuple> tuples); | ||
14 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java new file mode 100644 index 00000000..79e0526d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java | |||
@@ -0,0 +1,729 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | |||
11 | package tools.refinery.viatra.runtime.rete.network; | ||
12 | |||
13 | import org.apache.log4j.Logger; | ||
14 | import tools.refinery.viatra.runtime.CancellationToken; | ||
15 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
21 | import tools.refinery.viatra.runtime.rete.boundary.InputConnector; | ||
22 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
26 | import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker; | ||
27 | import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationTracker; | ||
28 | import tools.refinery.viatra.runtime.rete.network.delayed.DelayedCommand; | ||
29 | import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand; | ||
30 | import tools.refinery.viatra.runtime.rete.network.delayed.DelayedDisconnectCommand; | ||
31 | import tools.refinery.viatra.runtime.rete.remote.Address; | ||
32 | import tools.refinery.viatra.runtime.rete.single.SingleInputNode; | ||
33 | import tools.refinery.viatra.runtime.rete.single.TrimmerNode; | ||
34 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
35 | |||
36 | import java.util.*; | ||
37 | import java.util.function.Function; | ||
38 | |||
39 | /** | ||
40 | * @author Gabor Bergmann | ||
41 | * | ||
42 | * Mutexes: externalMessageLock - enlisting messages into and retrieving from the external message queue | ||
43 | * @since 2.2 | ||
44 | */ | ||
45 | public final class ReteContainer { | ||
46 | |||
47 | protected Thread consumerThread = null; | ||
48 | protected boolean killed = false; | ||
49 | |||
50 | protected Network network; | ||
51 | |||
52 | protected LinkedList<Clearable> clearables; | ||
53 | protected Map<Long, Node> nodesById; | ||
54 | protected long nextId = 0; | ||
55 | |||
56 | protected ConnectionFactory connectionFactory; | ||
57 | protected NodeProvisioner nodeProvisioner; | ||
58 | |||
59 | protected Deque<UpdateMessage> internalMessageQueue = new ArrayDeque<UpdateMessage>(); | ||
60 | protected/* volatile */Deque<UpdateMessage> externalMessageQueue = new ArrayDeque<UpdateMessage>(); | ||
61 | protected Object externalMessageLock = new Object(); | ||
62 | protected Long clock = 1L; // even: steady state, odd: active queue; access | ||
63 | // ONLY with messageQueue locked! | ||
64 | protected Map<ReteContainer, Long> terminationCriteria = null; | ||
65 | protected final Logger logger; | ||
66 | protected final CommunicationTracker tracker; | ||
67 | |||
68 | protected final IQueryBackendContext backendContext; | ||
69 | |||
70 | protected Set<DelayedCommand> delayedCommandQueue; | ||
71 | protected Set<DelayedCommand> delayedCommandBuffer; | ||
72 | protected boolean executingDelayedCommands; | ||
73 | |||
74 | protected final TimelyConfiguration timelyConfiguration; | ||
75 | |||
76 | private final CancellationToken cancellationToken; | ||
77 | |||
78 | /** | ||
79 | * @param threaded | ||
80 | * false if operating in a single-threaded environment | ||
81 | */ | ||
82 | public ReteContainer(Network network, boolean threaded) { | ||
83 | super(); | ||
84 | this.network = network; | ||
85 | this.backendContext = network.getEngine().getBackendContext(); | ||
86 | this.timelyConfiguration = network.getEngine().getTimelyConfiguration(); | ||
87 | cancellationToken = backendContext.getRuntimeContext().getCancellationToken(); | ||
88 | |||
89 | this.delayedCommandQueue = new LinkedHashSet<DelayedCommand>(); | ||
90 | this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>(); | ||
91 | this.executingDelayedCommands = false; | ||
92 | |||
93 | if (this.isTimelyEvaluation()) { | ||
94 | this.tracker = new TimelyCommunicationTracker(this.getTimelyConfiguration()); | ||
95 | } else { | ||
96 | this.tracker = new TimelessCommunicationTracker(); | ||
97 | } | ||
98 | |||
99 | this.nodesById = CollectionsFactory.createMap(); | ||
100 | this.clearables = new LinkedList<Clearable>(); | ||
101 | this.logger = network.getEngine().getLogger(); | ||
102 | |||
103 | this.connectionFactory = new ConnectionFactory(this); | ||
104 | this.nodeProvisioner = new NodeProvisioner(this); | ||
105 | |||
106 | if (threaded) { | ||
107 | this.terminationCriteria = CollectionsFactory.createMap(); | ||
108 | this.consumerThread = new Thread("Rete thread of " + ReteContainer.super.toString()) { | ||
109 | @Override | ||
110 | public void run() { | ||
111 | messageConsumptionCycle(); | ||
112 | } | ||
113 | }; | ||
114 | this.consumerThread.start(); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /** | ||
119 | * @since 2.4 | ||
120 | */ | ||
121 | public boolean isTimelyEvaluation() { | ||
122 | return this.timelyConfiguration != null; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * @since 2.4 | ||
127 | */ | ||
128 | public TimelyConfiguration getTimelyConfiguration() { | ||
129 | return this.timelyConfiguration; | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * @since 1.6 | ||
134 | * @return the communication graph of the nodes, incl. message scheduling | ||
135 | */ | ||
136 | public CommunicationTracker getCommunicationTracker() { | ||
137 | return tracker; | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * Stops this container. To be called by Network.kill() | ||
142 | */ | ||
143 | public void kill() { | ||
144 | killed = true; | ||
145 | if (consumerThread != null) | ||
146 | consumerThread.interrupt(); | ||
147 | } | ||
148 | |||
149 | /** | ||
150 | * Establishes connection between a supplier and a receiver node, regardless which container they are in. Assumption | ||
151 | * is that this container is the home of the receiver, but it is not strictly necessary. | ||
152 | * | ||
153 | * @param synchronise | ||
154 | * indicates whether the receiver should be synchronised to the current contents of the supplier | ||
155 | */ | ||
156 | public void connectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver, | ||
157 | boolean synchronise) { | ||
158 | if (!isLocal(receiver)) | ||
159 | receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise); | ||
160 | else { | ||
161 | Receiver child = resolveLocal(receiver); | ||
162 | connectRemoteSupplier(supplier, child, synchronise); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * Severs connection between a supplier and a receiver node, regardless which container they are in. Assumption is | ||
168 | * that this container is the home of the receiver, but it is not strictly necessary. | ||
169 | * | ||
170 | * @param desynchronise | ||
171 | * indicates whether the current contents of the supplier should be subtracted from the receiver | ||
172 | */ | ||
173 | public void disconnectRemoteNodes(Address<? extends Supplier> supplier, Address<? extends Receiver> receiver, | ||
174 | boolean desynchronise) { | ||
175 | if (!isLocal(receiver)) | ||
176 | receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise); | ||
177 | else { | ||
178 | Receiver child = resolveLocal(receiver); | ||
179 | disconnectRemoteSupplier(supplier, child, desynchronise); | ||
180 | } | ||
181 | } | ||
182 | |||
183 | /** | ||
184 | * Establishes connection between a remote supplier and a local receiver node. | ||
185 | * | ||
186 | * @param synchronise | ||
187 | * indicates whether the receiver should be synchronised to the current contents of the supplier | ||
188 | */ | ||
189 | public void connectRemoteSupplier(Address<? extends Supplier> supplier, Receiver receiver, boolean synchronise) { | ||
190 | Supplier parent = nodeProvisioner.asSupplier(supplier); | ||
191 | if (synchronise) | ||
192 | connectAndSynchronize(parent, receiver); | ||
193 | else | ||
194 | connect(parent, receiver); | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * Severs connection between a remote supplier and a local receiver node. | ||
199 | * | ||
200 | * @param desynchronise | ||
201 | * indicates whether the current contents of the supplier should be subtracted from the receiver | ||
202 | */ | ||
203 | public void disconnectRemoteSupplier(Address<? extends Supplier> supplier, Receiver receiver, | ||
204 | boolean desynchronise) { | ||
205 | Supplier parent = nodeProvisioner.asSupplier(supplier); | ||
206 | if (desynchronise) | ||
207 | disconnectAndDesynchronize(parent, receiver); | ||
208 | else | ||
209 | disconnect(parent, receiver); | ||
210 | } | ||
211 | |||
212 | /** | ||
213 | * Connects a receiver to a supplier | ||
214 | */ | ||
215 | public void connect(Supplier supplier, Receiver receiver) { | ||
216 | supplier.appendChild(receiver); | ||
217 | receiver.appendParent(supplier); | ||
218 | tracker.registerDependency(supplier, receiver); | ||
219 | } | ||
220 | |||
221 | /** | ||
222 | * Disconnects a receiver from a supplier | ||
223 | */ | ||
224 | public void disconnect(Supplier supplier, Receiver receiver) { | ||
225 | supplier.removeChild(receiver); | ||
226 | receiver.removeParent(supplier); | ||
227 | tracker.unregisterDependency(supplier, receiver); | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * @since 2.3 | ||
232 | */ | ||
233 | public boolean isExecutingDelayedCommands() { | ||
234 | return this.executingDelayedCommands; | ||
235 | } | ||
236 | |||
237 | /** | ||
238 | * @since 2.3 | ||
239 | */ | ||
240 | public Set<DelayedCommand> getDelayedCommandQueue() { | ||
241 | if (this.executingDelayedCommands) { | ||
242 | return this.delayedCommandBuffer; | ||
243 | } else { | ||
244 | return this.delayedCommandQueue; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * Connects a receiver to a remote supplier, and synchronizes it to the current contents of the supplier | ||
250 | */ | ||
251 | public void connectAndSynchronize(Supplier supplier, Receiver receiver) { | ||
252 | supplier.appendChild(receiver); | ||
253 | receiver.appendParent(supplier); | ||
254 | tracker.registerDependency(supplier, receiver); | ||
255 | getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, receiver, this)); | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * Disconnects a receiver from a supplier | ||
260 | */ | ||
261 | public void disconnectAndDesynchronize(Supplier supplier, Receiver receiver) { | ||
262 | final boolean wasInSameSCC = this.isTimelyEvaluation() && this.tracker.areInSameGroup(supplier, receiver); | ||
263 | supplier.removeChild(receiver); | ||
264 | receiver.removeParent(supplier); | ||
265 | tracker.unregisterDependency(supplier, receiver); | ||
266 | getDelayedCommandQueue().add(new DelayedDisconnectCommand(supplier, receiver, this, wasInSameSCC)); | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * @since 2.3 | ||
271 | */ | ||
272 | public void executeDelayedCommands() { | ||
273 | if (!this.delayedCommandQueue.isEmpty()) { | ||
274 | flushUpdates(); | ||
275 | this.executingDelayedCommands = true; | ||
276 | for (final DelayedCommand command : this.delayedCommandQueue) { | ||
277 | command.run(); | ||
278 | } | ||
279 | this.delayedCommandQueue = this.delayedCommandBuffer; | ||
280 | this.delayedCommandBuffer = new LinkedHashSet<DelayedCommand>(); | ||
281 | flushUpdates(); | ||
282 | this.executingDelayedCommands = false; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | /** | ||
287 | * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is | ||
288 | * indicated by the Address. Designed to be called by the Network, DO NOT use in any other way. @pre: | ||
289 | * address.container == this, e.g. address MUST be local | ||
290 | * | ||
291 | * @return the value of the container's clock at the time when the message was accepted into the local message queue | ||
292 | */ | ||
293 | long sendUpdateToLocalAddress(Address<? extends Receiver> address, Direction direction, Tuple updateElement) { | ||
294 | long timestamp; | ||
295 | Receiver receiver = resolveLocal(address); | ||
296 | UpdateMessage message = new UpdateMessage(receiver, direction, updateElement); | ||
297 | synchronized (externalMessageLock) { | ||
298 | externalMessageQueue.add(message); | ||
299 | timestamp = clock; | ||
300 | externalMessageLock.notifyAll(); | ||
301 | } | ||
302 | |||
303 | return timestamp; | ||
304 | |||
305 | } | ||
306 | |||
307 | /** | ||
308 | * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial | ||
309 | * matching. The receiver is indicated by the Address. Designed to be called by the Network, DO NOT use in any other | ||
310 | * way. @pre: address.container == this, e.g. address MUST be local @pre: updateElements is nonempty! | ||
311 | * | ||
312 | * @return the value of the container's clock at the time when the message was accepted into the local message queue | ||
313 | */ | ||
314 | long sendUpdatesToLocalAddress(Address<? extends Receiver> address, Direction direction, | ||
315 | Collection<Tuple> updateElements) { | ||
316 | |||
317 | long timestamp; | ||
318 | Receiver receiver = resolveLocal(address); | ||
319 | // UpdateMessage message = new UpdateMessage(receiver, direction, | ||
320 | // updateElement); | ||
321 | synchronized (externalMessageLock) { | ||
322 | for (Tuple ps : updateElements) | ||
323 | externalMessageQueue.add(new UpdateMessage(receiver, direction, ps)); | ||
324 | // messageQueue.add(new UpdateMessage(resolveLocal(address), | ||
325 | // direction, updateElement)); | ||
326 | // this.sendUpdateInternal(resolveLocal(address), direction, | ||
327 | // updateElement); | ||
328 | timestamp = clock; | ||
329 | externalMessageLock.notifyAll(); | ||
330 | } | ||
331 | |||
332 | return timestamp; | ||
333 | } | ||
334 | |||
335 | /** | ||
336 | * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is | ||
337 | * indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT use in any | ||
338 | * other way. | ||
339 | */ | ||
340 | void sendUpdateToLocalAddressSingleThreaded(Address<? extends Receiver> address, Direction direction, | ||
341 | Tuple updateElement) { | ||
342 | Receiver receiver = resolveLocal(address); | ||
343 | UpdateMessage message = new UpdateMessage(receiver, direction, updateElement); | ||
344 | internalMessageQueue.add(message); | ||
345 | } | ||
346 | |||
347 | /** | ||
348 | * Sends multiple update messages to the receiver node, indicating a newly found or lost partial matching. The | ||
349 | * receiver is indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT | ||
350 | * use in any other way. | ||
351 | * | ||
352 | * @pre: address.container == this, e.g. address MUST be local | ||
353 | */ | ||
354 | void sendUpdatesToLocalAddressSingleThreaded(Address<? extends Receiver> address, Direction direction, | ||
355 | Collection<Tuple> updateElements) { | ||
356 | Receiver receiver = resolveLocal(address); | ||
357 | for (Tuple ps : updateElements) | ||
358 | internalMessageQueue.add(new UpdateMessage(receiver, direction, ps)); | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * Sends an update message to a node in a different container. The receiver is indicated by the Address. Designed to | ||
363 | * be called by RemoteReceivers, DO NOT use in any other way. | ||
364 | * | ||
365 | * @since 2.4 | ||
366 | */ | ||
367 | public void sendUpdateToRemoteAddress(Address<? extends Receiver> address, Direction direction, | ||
368 | Tuple updateElement) { | ||
369 | ReteContainer otherContainer = address.getContainer(); | ||
370 | long otherClock = otherContainer.sendUpdateToLocalAddress(address, direction, updateElement); | ||
371 | // Long criterion = terminationCriteria.get(otherContainer); | ||
372 | // if (criterion==null || otherClock > criterion) | ||
373 | terminationCriteria.put(otherContainer, otherClock); | ||
374 | } | ||
375 | |||
376 | /** | ||
377 | * Finalises all update sequences and returns. To be called from user threads (e.g. network construction). | ||
378 | */ | ||
379 | public void flushUpdates() { | ||
380 | network.waitForReteTermination(); | ||
381 | // synchronized (messageQueue) | ||
382 | // { | ||
383 | // while (!messageQueue.isEmpty()) | ||
384 | // { | ||
385 | // try { | ||
386 | // UpdateMessage message = messageQueue.take(); | ||
387 | // message.receiver.update(message.direction, message.updateElement); | ||
388 | // } catch (InterruptedException e) {} | ||
389 | // } | ||
390 | // } | ||
391 | } | ||
392 | |||
393 | /** | ||
394 | * Retrieves a safe copy of the contents of a supplier. | ||
395 | * | ||
396 | * <p> Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set. | ||
397 | * | ||
398 | * @param flush if true, a flush is performed before pulling the contents | ||
399 | * @since 2.3 | ||
400 | */ | ||
401 | public Collection<Tuple> pullContents(final Supplier supplier, final boolean flush) { | ||
402 | if (flush) { | ||
403 | flushUpdates(); | ||
404 | } | ||
405 | final Collection<Tuple> collector = new ArrayList<Tuple>(); | ||
406 | supplier.pullInto(collector, flush); | ||
407 | return collector; | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * @since 2.4 | ||
412 | */ | ||
413 | public Map<Tuple, Timeline<Timestamp>> pullContentsWithTimeline(final Supplier supplier, final boolean flush) { | ||
414 | if (flush) { | ||
415 | flushUpdates(); | ||
416 | } | ||
417 | final Map<Tuple, Timeline<Timestamp>> collector = CollectionsFactory.createMap(); | ||
418 | supplier.pullIntoWithTimeline(collector, flush); | ||
419 | return collector; | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * Retrieves the contents of a SingleInputNode's parentage. | ||
424 | * | ||
425 | * @since 2.3 | ||
426 | */ | ||
427 | public Collection<Tuple> pullPropagatedContents(final SingleInputNode supplier, final boolean flush) { | ||
428 | if (flush) { | ||
429 | flushUpdates(); | ||
430 | } | ||
431 | final Collection<Tuple> collector = new LinkedList<Tuple>(); | ||
432 | supplier.propagatePullInto(collector, flush); | ||
433 | return collector; | ||
434 | } | ||
435 | |||
436 | /** | ||
437 | * Retrieves the timestamp-aware contents of a SingleInputNode's parentage. | ||
438 | * | ||
439 | * @since 2.3 | ||
440 | */ | ||
441 | public Map<Tuple, Timeline<Timestamp>> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier, | ||
442 | final boolean flush) { | ||
443 | if (flush) { | ||
444 | flushUpdates(); | ||
445 | } | ||
446 | final Map<Tuple, Timeline<Timestamp>> collector = CollectionsFactory.createMap(); | ||
447 | supplier.propagatePullIntoWithTimestamp(collector, flush); | ||
448 | return collector; | ||
449 | } | ||
450 | |||
451 | /** | ||
452 | * Retrieves the contents of a supplier for a remote caller. Assumption is that this container is the home of the | ||
453 | * supplier, but it is not strictly necessary. | ||
454 | * | ||
455 | * @param supplier | ||
456 | * the address of the supplier to be pulled. | ||
457 | * @since 2.3 | ||
458 | */ | ||
459 | public Collection<Tuple> remotePull(Address<? extends Supplier> supplier, boolean flush) { | ||
460 | if (!isLocal(supplier)) | ||
461 | return supplier.getContainer().remotePull(supplier, flush); | ||
462 | return pullContents(resolveLocal(supplier), flush); | ||
463 | } | ||
464 | |||
465 | /** | ||
466 | * Proxies for the getPosMapping() of Production nodes. Retrieves the posmapping of a remote or local Production to | ||
467 | * a remote or local caller. | ||
468 | */ | ||
469 | public Map<String, Integer> remotePosMapping(Address<? extends ProductionNode> production) { | ||
470 | if (!isLocal(production)) | ||
471 | return production.getContainer().remotePosMapping(production); | ||
472 | return resolveLocal(production).getPosMapping(); | ||
473 | } | ||
474 | |||
475 | /** | ||
476 | * Continually consumes update messages. Should be run on a dedicated thread. | ||
477 | */ | ||
478 | void messageConsumptionCycle() { | ||
479 | while (!killed) // deliver messages on and on and on.... | ||
480 | { | ||
481 | long incrementedClock = 0; | ||
482 | UpdateMessage message = null; | ||
483 | |||
484 | if (!internalMessageQueue.isEmpty()) // take internal messages first | ||
485 | message = internalMessageQueue.removeFirst(); | ||
486 | else | ||
487 | // no internal message, take an incoming message | ||
488 | synchronized (externalMessageLock) { // no sleeping allowed, | ||
489 | // because external | ||
490 | // queue is locked for | ||
491 | // precise clocking of | ||
492 | // termination point! | ||
493 | if (!externalMessageQueue.isEmpty()) { // if external queue | ||
494 | // is non-empty, | ||
495 | // retrieve the next | ||
496 | // message instantly | ||
497 | message = takeExternalMessage(); | ||
498 | } else { // if external queue is found empty (and this is | ||
499 | // the first time in a row) | ||
500 | incrementedClock = ++clock; // local termination point | ||
501 | // synchronized(clock){incrementedClock = ++clock;} | ||
502 | } | ||
503 | } | ||
504 | |||
505 | if (message == null) // both queues were empty | ||
506 | { | ||
507 | localUpdateTermination(incrementedClock); // report local | ||
508 | // termination point | ||
509 | while (message == null) // wait for a message while external | ||
510 | // queue is still empty | ||
511 | { | ||
512 | synchronized (externalMessageLock) { | ||
513 | while (externalMessageQueue.isEmpty()) { | ||
514 | try { | ||
515 | externalMessageLock.wait(); | ||
516 | } catch (InterruptedException e) { | ||
517 | if (killed) | ||
518 | return; | ||
519 | } | ||
520 | } | ||
521 | message = takeExternalMessage(); | ||
522 | } | ||
523 | |||
524 | } | ||
525 | } | ||
526 | |||
527 | // now we have a message to deliver | ||
528 | // NOTE: this method is not compatible with differential dataflow | ||
529 | message.receiver.update(message.direction, message.updateElement, Timestamp.ZERO); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | /** | ||
534 | * @since 1.6 | ||
535 | */ | ||
536 | public static final Function<Node, String> NAME_MAPPER = input -> input.toString().substring(0, | ||
537 | Math.min(30, input.toString().length())); | ||
538 | |||
539 | /** | ||
540 | * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker. | ||
541 | * | ||
542 | * @since 1.6 | ||
543 | */ | ||
544 | public void deliverMessagesSingleThreaded() { | ||
545 | if (!backendContext.areUpdatesDelayed()) { | ||
546 | if (Options.MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING) { | ||
547 | // known unreachable; enable for debugging only | ||
548 | |||
549 | CommunicationGroup lastGroup = null; | ||
550 | Set<CommunicationGroup> seenInThisCycle = new HashSet<>(); | ||
551 | |||
552 | while (!tracker.isEmpty()) { | ||
553 | final CommunicationGroup group = tracker.getAndRemoveFirstGroup(); | ||
554 | |||
555 | /** | ||
556 | * The current group does not violate the communication schema iff (1) it was not seen before OR (2) | ||
557 | * the last one that was seen is exactly the same as the current one this can happen if the group | ||
558 | * was added back because of in-group message passing | ||
559 | */ | ||
560 | boolean okGroup = (group == lastGroup) || seenInThisCycle.add(group); | ||
561 | |||
562 | if (!okGroup) { | ||
563 | logger.error( | ||
564 | "[INTERNAL ERROR] Violation of communication schema! The communication component with representative " | ||
565 | + group.getRepresentative() + " has already been processed!"); | ||
566 | } | ||
567 | |||
568 | group.deliverMessages(); | ||
569 | |||
570 | lastGroup = group; | ||
571 | } | ||
572 | |||
573 | } else { | ||
574 | while (!tracker.isEmpty()) { | ||
575 | final CommunicationGroup group = tracker.getAndRemoveFirstGroup(); | ||
576 | group.deliverMessages(); | ||
577 | } | ||
578 | } | ||
579 | } | ||
580 | } | ||
581 | |||
582 | private void localUpdateTermination(long incrementedClock) { | ||
583 | network.reportLocalUpdateTermination(this, incrementedClock, terminationCriteria); | ||
584 | terminationCriteria.clear(); | ||
585 | |||
586 | // synchronized(clock){++clock;} // +1 incrementing for parity and easy | ||
587 | // comparison | ||
588 | } | ||
589 | |||
590 | // @pre: externalMessageQueue synchronized && nonempty | ||
591 | private UpdateMessage takeExternalMessage() { | ||
592 | UpdateMessage message = externalMessageQueue.removeFirst(); | ||
593 | if (!externalMessageQueue.isEmpty()) { // copy the whole queue over | ||
594 | // for speedup | ||
595 | Deque<UpdateMessage> temp = externalMessageQueue; | ||
596 | externalMessageQueue = internalMessageQueue; | ||
597 | internalMessageQueue = temp; | ||
598 | } | ||
599 | return message; | ||
600 | } | ||
601 | |||
602 | /** | ||
603 | * Provides an external address for the selected node. | ||
604 | * | ||
605 | * @pre node belongs to this container. | ||
606 | */ | ||
607 | public <N extends Node> Address<N> makeAddress(N node) { | ||
608 | return new Address<N>(node); | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * Checks whether a certain address points to a node at this container. | ||
613 | */ | ||
614 | public boolean isLocal(Address<? extends Node> address) { | ||
615 | return address.getContainer() == this; | ||
616 | } | ||
617 | |||
618 | /** | ||
619 | * Returns an addressed node at this container. | ||
620 | * | ||
621 | * @pre: address.container == this, e.g. address MUST be local | ||
622 | * @throws IllegalArgumentException | ||
623 | * if address is non-local | ||
624 | */ | ||
625 | @SuppressWarnings("unchecked") | ||
626 | public <N extends Node> N resolveLocal(Address<N> address) { | ||
627 | if (this != address.getContainer()) | ||
628 | throw new IllegalArgumentException(String.format("Address %s non-local at container %s", address, this)); | ||
629 | |||
630 | N cached = address.getNodeCache(); | ||
631 | if (cached != null) | ||
632 | return cached; | ||
633 | else { | ||
634 | N node = (N) nodesById.get(address.getNodeId()); | ||
635 | address.setNodeCache(node); | ||
636 | return node; | ||
637 | } | ||
638 | } | ||
639 | |||
640 | /** | ||
641 | * Registers a node into the rete network (should be called by constructor). Every node MUST be registered by its | ||
642 | * constructor. | ||
643 | * | ||
644 | * @return the unique nodeId issued to the node. | ||
645 | */ | ||
646 | public long registerNode(Node n) { | ||
647 | long id = nextId++; | ||
648 | nodesById.put(id, n); | ||
649 | return id; | ||
650 | } | ||
651 | |||
652 | /** | ||
653 | * Unregisters a node from the rete network. Do NOT call if node is still connected to other Nodes, or Adressed or | ||
654 | * otherwise referenced. | ||
655 | */ | ||
656 | public void unregisterNode(Node n) { | ||
657 | nodesById.remove(n.getNodeId()); | ||
658 | } | ||
659 | |||
660 | /** | ||
661 | * Registers a pattern memory into the rete network. Every memory MUST be registered by its owner node. | ||
662 | */ | ||
663 | public void registerClearable(Clearable c) { | ||
664 | clearables.addFirst(c); | ||
665 | } | ||
666 | |||
667 | /** | ||
668 | * Unregisters a pattern memory from the rete network. | ||
669 | */ | ||
670 | public void unregisterClearable(Clearable c) { | ||
671 | clearables.remove(c); | ||
672 | } | ||
673 | |||
674 | /** | ||
675 | * Clears all memory contents in the network. Reverts to initial state. | ||
676 | */ | ||
677 | public void clearAll() { | ||
678 | for (Clearable c : clearables) { | ||
679 | c.clear(); | ||
680 | } | ||
681 | } | ||
682 | |||
683 | public NodeFactory getNodeFactory() { | ||
684 | return network.getNodeFactory(); | ||
685 | } | ||
686 | |||
687 | public ConnectionFactory getConnectionFactory() { | ||
688 | return connectionFactory; | ||
689 | } | ||
690 | |||
691 | public NodeProvisioner getProvisioner() { | ||
692 | return nodeProvisioner; | ||
693 | } | ||
694 | |||
695 | public Network getNetwork() { | ||
696 | return network; | ||
697 | } | ||
698 | |||
699 | @Override | ||
700 | public String toString() { | ||
701 | StringBuilder sb = new StringBuilder(); | ||
702 | String separator = System.getProperty("line.separator"); | ||
703 | sb.append(super.toString() + "[[[" + separator); | ||
704 | java.util.List<Long> keys = new java.util.ArrayList<Long>(nodesById.keySet()); | ||
705 | java.util.Collections.sort(keys); | ||
706 | for (Long key : keys) { | ||
707 | sb.append(key + " -> " + nodesById.get(key) + separator); | ||
708 | } | ||
709 | sb.append("]]] of " + network); | ||
710 | return sb.toString(); | ||
711 | } | ||
712 | |||
713 | /** | ||
714 | * Access all the Rete nodes inside this container. | ||
715 | * | ||
716 | * @return the collection of {@link Node} instances | ||
717 | */ | ||
718 | public Collection<Node> getAllNodes() { | ||
719 | return nodesById.values(); | ||
720 | } | ||
721 | |||
722 | public InputConnector getInputConnectionFactory() { | ||
723 | return network.getInputConnector(); | ||
724 | } | ||
725 | |||
726 | public void checkCancelled() { | ||
727 | cancellationToken.checkCancelled(); | ||
728 | } | ||
729 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java new file mode 100644 index 00000000..7dc7c4bc --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java | |||
@@ -0,0 +1,123 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | |||
11 | package tools.refinery.viatra.runtime.rete.network; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.rete.index.GenericProjectionIndexer; | ||
18 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
21 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
22 | |||
23 | import java.util.Collection; | ||
24 | import java.util.HashSet; | ||
25 | import java.util.List; | ||
26 | import java.util.Set; | ||
27 | |||
28 | /** | ||
29 | * Base implementation for a supplier node. | ||
30 | * | ||
31 | * @author Gabor Bergmann | ||
32 | * | ||
33 | */ | ||
34 | public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode { | ||
35 | protected final List<Receiver> children = CollectionsFactory.createObserverList(); | ||
36 | /** | ||
37 | * @since 2.2 | ||
38 | */ | ||
39 | protected final List<Mailbox> childMailboxes = CollectionsFactory.createObserverList(); | ||
40 | |||
41 | public StandardNode(final ReteContainer reteContainer) { | ||
42 | super(reteContainer); | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * @since 2.4 | ||
47 | */ | ||
48 | protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { | ||
49 | reteContainer.checkCancelled(); | ||
50 | for (final Mailbox childMailbox : childMailboxes) { | ||
51 | childMailbox.postMessage(direction, updateElement, timestamp); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public void appendChild(final Receiver receiver) { | ||
57 | children.add(receiver); | ||
58 | childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox())); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public void removeChild(final Receiver receiver) { | ||
63 | children.remove(receiver); | ||
64 | Mailbox mailboxToRemove = null; | ||
65 | for (final Mailbox mailbox : childMailboxes) { | ||
66 | if (mailbox.getReceiver() == receiver) { | ||
67 | mailboxToRemove = mailbox; | ||
68 | break; | ||
69 | } | ||
70 | } | ||
71 | assert mailboxToRemove != null; | ||
72 | childMailboxes.remove(mailboxToRemove); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public void networkStructureChanged() { | ||
77 | childMailboxes.clear(); | ||
78 | for (final Receiver receiver : children) { | ||
79 | childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox())); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public Collection<Receiver> getReceivers() { | ||
85 | return children; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * @since 2.2 | ||
90 | */ | ||
91 | public Collection<Mailbox> getChildMailboxes() { | ||
92 | return this.childMailboxes; | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public Set<Tuple> getPulledContents(final boolean flush) { | ||
97 | final HashSet<Tuple> results = new HashSet<Tuple>(); | ||
98 | pullInto(results, flush); | ||
99 | return results; | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) { | ||
104 | final GenericProjectionIndexer indexer = new GenericProjectionIndexer(reteContainer, mask); | ||
105 | for (final TraceInfo traceInfo : traces) { | ||
106 | indexer.assignTraceInfo(traceInfo); | ||
107 | } | ||
108 | reteContainer.connectAndSynchronize(this, indexer); | ||
109 | return indexer; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * @since 1.6 | ||
114 | */ | ||
115 | protected void issueError(final String message, final Exception ex) { | ||
116 | if (ex == null) { | ||
117 | this.reteContainer.getNetwork().getEngine().getLogger().error(message); | ||
118 | } else { | ||
119 | this.reteContainer.getNetwork().getEngine().getLogger().error(message, ex); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java new file mode 100644 index 00000000..1917a7cf --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java | |||
@@ -0,0 +1,82 @@ | |||
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.rete.network; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
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.timeline.Timeline; | ||
19 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | import tools.refinery.viatra.runtime.rete.single.TrimmerNode; | ||
22 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
23 | |||
24 | /** | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | * A supplier is an object that can propagate insert or revoke events towards receivers. | ||
28 | */ | ||
29 | public interface Supplier extends Node { | ||
30 | |||
31 | /** | ||
32 | * Pulls the contents of this object in this particular moment into a target collection. | ||
33 | * | ||
34 | * @param flush if true, flushing of messages is allowed during the pull, otherwise flushing is not allowed | ||
35 | * @since 2.3 | ||
36 | */ | ||
37 | public void pullInto(Collection<Tuple> collector, boolean flush); | ||
38 | |||
39 | /** | ||
40 | * @since 2.4 | ||
41 | */ | ||
42 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush); | ||
43 | |||
44 | /** | ||
45 | * Returns the contents of this object in this particular moment. | ||
46 | * For memoryless nodes, this may involve a costly recomputation of contents. | ||
47 | * | ||
48 | * The result is returned as a Set, even when it has multiplicities (at the output of {@link TrimmerNode}). | ||
49 | * | ||
50 | * <p> Intended mainly for debug purposes; therefore flushing is performed only if explicitly requested | ||
51 | * During runtime, flushing may be preferred; see {@link ReteContainer#pullContents(Supplier)} | ||
52 | * @since 2.3 | ||
53 | */ | ||
54 | public Set<Tuple> getPulledContents(boolean flush); | ||
55 | |||
56 | default public Set<Tuple> getPulledContents() { | ||
57 | return getPulledContents(true); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * appends a receiver that will continously receive insert and revoke updates from this supplier | ||
62 | */ | ||
63 | void appendChild(Receiver receiver); | ||
64 | |||
65 | /** | ||
66 | * removes a receiver | ||
67 | */ | ||
68 | void removeChild(Receiver receiver); | ||
69 | |||
70 | /** | ||
71 | * Instantiates (or reuses, depending on implementation) an index according to the given mask. | ||
72 | * | ||
73 | * Intended for internal use; clients should invoke through Library instead to enable reusing. | ||
74 | */ | ||
75 | ProjectionIndexer constructIndex(TupleMask mask, TraceInfo... traces); | ||
76 | |||
77 | /** | ||
78 | * lists receivers | ||
79 | */ | ||
80 | Collection<Receiver> getReceivers(); | ||
81 | |||
82 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java new file mode 100644 index 00000000..f238f47b --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java | |||
@@ -0,0 +1,19 @@ | |||
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.rete.network; | ||
11 | |||
12 | /** | ||
13 | * @author Gabor Bergmann | ||
14 | * | ||
15 | * A Tunnel is an interface into which elments can be instered and from which productions can be extracted. | ||
16 | */ | ||
17 | public interface Tunnel extends Supplier, Receiver { | ||
18 | |||
19 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java new file mode 100644 index 00000000..1334a3a9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java | |||
@@ -0,0 +1,31 @@ | |||
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.rete.network; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
14 | |||
15 | class UpdateMessage { | ||
16 | public Receiver receiver; | ||
17 | public Direction direction; | ||
18 | public Tuple updateElement; | ||
19 | |||
20 | public UpdateMessage(Receiver receiver, Direction direction, Tuple updateElement) { | ||
21 | this.receiver = receiver; | ||
22 | this.direction = direction; | ||
23 | this.updateElement = updateElement; | ||
24 | } | ||
25 | |||
26 | @Override | ||
27 | public String toString() { | ||
28 | return "M." + direction + ": " + updateElement + " -> " + receiver; | ||
29 | } | ||
30 | |||
31 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java new file mode 100644 index 00000000..8cedeb11 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java | |||
@@ -0,0 +1,103 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, 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.rete.network.communication; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
15 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
16 | |||
17 | /** | ||
18 | * A communication group represents a set of nodes in the communication graph that form a strongly connected component. | ||
19 | * | ||
20 | * @author Tamas Szabo | ||
21 | * @since 1.6 | ||
22 | */ | ||
23 | public abstract class CommunicationGroup implements Comparable<CommunicationGroup> { | ||
24 | |||
25 | public static final String UNSUPPORTED_MESSAGE_KIND = "Unsupported message kind "; | ||
26 | |||
27 | /** | ||
28 | * Marker for the {@link CommunicationTracker} | ||
29 | */ | ||
30 | public boolean isEnqueued = false; | ||
31 | |||
32 | protected final Node representative; | ||
33 | |||
34 | /** | ||
35 | * May be changed during bumping in {@link CommunicationTracker.registerDependency} | ||
36 | */ | ||
37 | protected int identifier; | ||
38 | |||
39 | /** | ||
40 | * @since 1.7 | ||
41 | */ | ||
42 | protected final CommunicationTracker tracker; | ||
43 | |||
44 | /** | ||
45 | * @since 1.7 | ||
46 | */ | ||
47 | public CommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) { | ||
48 | this.tracker = tracker; | ||
49 | this.representative = representative; | ||
50 | this.identifier = identifier; | ||
51 | } | ||
52 | |||
53 | public abstract void deliverMessages(); | ||
54 | |||
55 | public Node getRepresentative() { | ||
56 | return representative; | ||
57 | } | ||
58 | |||
59 | public abstract boolean isEmpty(); | ||
60 | |||
61 | /** | ||
62 | * @since 2.0 | ||
63 | */ | ||
64 | public abstract void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind); | ||
65 | |||
66 | /** | ||
67 | * @since 2.0 | ||
68 | */ | ||
69 | public abstract void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind); | ||
70 | |||
71 | public abstract Map<MessageSelector, Collection<Mailbox>> getMailboxes(); | ||
72 | |||
73 | public abstract boolean isRecursive(); | ||
74 | |||
75 | @Override | ||
76 | public int hashCode() { | ||
77 | return this.identifier; | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public String toString() { | ||
82 | return this.getClass().getSimpleName() + " " + this.identifier + " - representative: " + this.representative | ||
83 | + " - isEmpty: " + isEmpty(); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public boolean equals(final Object obj) { | ||
88 | if (obj == null || this.getClass() != obj.getClass()) { | ||
89 | return false; | ||
90 | } else if (this == obj) { | ||
91 | return true; | ||
92 | } else { | ||
93 | final CommunicationGroup that = (CommunicationGroup) obj; | ||
94 | return this.identifier == that.identifier; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | @Override | ||
99 | public int compareTo(final CommunicationGroup that) { | ||
100 | return this.identifier - that.identifier; | ||
101 | } | ||
102 | |||
103 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java new file mode 100644 index 00000000..d244e644 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java | |||
@@ -0,0 +1,467 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, 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.rete.network.communication; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.PriorityQueue; | ||
16 | import java.util.Queue; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg; | ||
20 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting; | ||
21 | import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; | ||
22 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
23 | import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode; | ||
24 | import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode; | ||
25 | import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode; | ||
26 | import tools.refinery.viatra.runtime.rete.index.DualInputNode; | ||
27 | import tools.refinery.viatra.runtime.rete.index.ExistenceNode; | ||
28 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
29 | import tools.refinery.viatra.runtime.rete.index.IndexerListener; | ||
30 | import tools.refinery.viatra.runtime.rete.index.IterableIndexer; | ||
31 | import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer; | ||
32 | import tools.refinery.viatra.runtime.rete.network.IGroupable; | ||
33 | import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; | ||
34 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
35 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
36 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
37 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
38 | import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyIndexerListenerProxy; | ||
39 | import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy; | ||
40 | import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox; | ||
41 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
42 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
43 | import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode; | ||
44 | import tools.refinery.viatra.runtime.rete.single.TrimmerNode; | ||
45 | |||
46 | /** | ||
47 | * An instance of this class is associated with every {@link ReteContainer}. The tracker serves two purposes: <br> | ||
48 | * (1) It allows RETE nodes to register their communication dependencies on-the-fly. These dependencies can be | ||
49 | * registered or unregistered when nodes are disposed of. <br> | ||
50 | * (2) It allows RETE nodes to register their mailboxes as dirty, that is, they can tell the tracker that they have | ||
51 | * something to send to other nodes in the network. The tracker is then responsible for ordering these messages (more | ||
52 | * precisely, the mailboxes that contain the messages) for the associated {@link ReteContainer}. The ordering is | ||
53 | * governed by the strongly connected components in the dependency network and follows a topological sorting scheme; | ||
54 | * those mailboxes will be emptied first whose owner nodes do not depend on other undelivered messages. | ||
55 | * | ||
56 | * @author Tamas Szabo | ||
57 | * @since 1.6 | ||
58 | * | ||
59 | */ | ||
60 | public abstract class CommunicationTracker { | ||
61 | |||
62 | /** | ||
63 | * The minimum group id assigned so far | ||
64 | */ | ||
65 | protected int minGroupId; | ||
66 | |||
67 | /** | ||
68 | * The maximum group id assigned so far | ||
69 | */ | ||
70 | protected int maxGroupId; | ||
71 | |||
72 | /** | ||
73 | * The dependency graph of the communications in the RETE network | ||
74 | */ | ||
75 | protected final Graph<Node> dependencyGraph; | ||
76 | |||
77 | /** | ||
78 | * Incremental SCC information about the dependency graph | ||
79 | */ | ||
80 | protected final IncSCCAlg<Node> sccInformationProvider; | ||
81 | |||
82 | /** | ||
83 | * Precomputed node -> communication group map | ||
84 | */ | ||
85 | protected final Map<Node, CommunicationGroup> groupMap; | ||
86 | |||
87 | /** | ||
88 | * Priority queue of active communication groups | ||
89 | */ | ||
90 | protected final Queue<CommunicationGroup> groupQueue; | ||
91 | |||
92 | // groups should have a simple integer flag which represents its position in a priority queue | ||
93 | // priority queue only contains the ACTIVE groups | ||
94 | |||
95 | public CommunicationTracker() { | ||
96 | this.dependencyGraph = new Graph<Node>(); | ||
97 | this.sccInformationProvider = new IncSCCAlg<Node>(this.dependencyGraph); | ||
98 | this.groupQueue = new PriorityQueue<CommunicationGroup>(); | ||
99 | this.groupMap = new HashMap<Node, CommunicationGroup>(); | ||
100 | } | ||
101 | |||
102 | public Graph<Node> getDependencyGraph() { | ||
103 | return dependencyGraph; | ||
104 | } | ||
105 | |||
106 | public CommunicationGroup getGroup(final Node node) { | ||
107 | return this.groupMap.get(node); | ||
108 | } | ||
109 | |||
110 | private void precomputeGroups() { | ||
111 | groupMap.clear(); | ||
112 | |||
113 | // reconstruct group map from dependency graph | ||
114 | final Graph<Node> reducedGraph = sccInformationProvider.getReducedGraph(); | ||
115 | final List<Node> representatives = TopologicalSorting.compute(reducedGraph); | ||
116 | |||
117 | for (int i = 0; i < representatives.size(); i++) { // groups for SCC representatives | ||
118 | final Node representative = representatives.get(i); | ||
119 | createAndStoreGroup(representative, i); | ||
120 | } | ||
121 | |||
122 | minGroupId = 0; | ||
123 | maxGroupId = representatives.size() - 1; | ||
124 | |||
125 | for (final Node node : dependencyGraph.getAllNodes()) { // extend group map to the rest of nodes | ||
126 | final Node representative = sccInformationProvider.getRepresentative(node); | ||
127 | final CommunicationGroup group = groupMap.get(representative); | ||
128 | if (representative != node) { | ||
129 | addToGroup(node, group); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | for (final Node node : dependencyGraph.getAllNodes()) { | ||
134 | // set fall-through flags of default mailboxes | ||
135 | precomputeFallThroughFlag(node); | ||
136 | // perform further tracker-specific post-processing | ||
137 | postProcessNode(node); | ||
138 | } | ||
139 | |||
140 | // reconstruct new queue contents based on new group map | ||
141 | if (!groupQueue.isEmpty()) { | ||
142 | final Set<CommunicationGroup> oldActiveGroups = new HashSet<CommunicationGroup>(groupQueue); | ||
143 | groupQueue.clear(); | ||
144 | reconstructQueueContents(oldActiveGroups); | ||
145 | } | ||
146 | |||
147 | // post process the groups | ||
148 | for (final CommunicationGroup group : groupMap.values()) { | ||
149 | postProcessGroup(group); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * This method is responsible for reconstructing the active queue contents after the network structure has changed. | ||
155 | * It it defined as abstract because the reconstruction logic is specific to each {@link CommunicationTracker}. | ||
156 | * @since 2.4 | ||
157 | */ | ||
158 | protected abstract void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups); | ||
159 | |||
160 | private void addToGroup(final Node node, final CommunicationGroup group) { | ||
161 | groupMap.put(node, group); | ||
162 | if (node instanceof Receiver) { | ||
163 | ((Receiver) node).getMailbox().setCurrentGroup(group); | ||
164 | if (node instanceof IGroupable) { | ||
165 | ((IGroupable) node).setCurrentGroup(group); | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Depends on the groups, as well as the parent nodes of the argument, so recomputation is needed if these change | ||
172 | */ | ||
173 | private void precomputeFallThroughFlag(final Node node) { | ||
174 | CommunicationGroup group = groupMap.get(node); | ||
175 | if (node instanceof Receiver) { | ||
176 | IGroupable mailbox = ((Receiver) node).getMailbox(); | ||
177 | if (mailbox instanceof FallThroughCapableMailbox) { | ||
178 | Set<Node> directParents = dependencyGraph.getSourceNodes(node).distinctValues(); | ||
179 | // decide between using quick&cheap fall-through, or allowing for update cancellation | ||
180 | boolean fallThrough = | ||
181 | // disallow fallthrough: updates at production nodes should cancel, if they can be trimmed or | ||
182 | // disjunctive | ||
183 | (!(node instanceof ProductionNode && ( // it is a production node... | ||
184 | // with more than one parent | ||
185 | directParents.size() > 0 || | ||
186 | // or true trimming in its sole parent | ||
187 | directParents.size() == 1 && trueTrimming(directParents.iterator().next())))) && | ||
188 | // disallow fallthrough: external updates should be stored (if updates are delayed) | ||
189 | (!(node instanceof ExternalInputEnumeratorNode)) && | ||
190 | // disallow fallthrough: RelationEvaluatorNode needs to be notified in batch-style, and the batching is done by the mailbox | ||
191 | // however, it is not the RelationEvaluatorNode itself that is interesting here, as that indirectly uses the BatchingReceiver | ||
192 | // so we need to disable fall-through for the BatchingReceiver | ||
193 | (!(node instanceof RelationEvaluatorNode.BatchingReceiver)); | ||
194 | // do additional checks | ||
195 | if (fallThrough) { | ||
196 | // recursive parent groups generate excess updates that should be cancelled after delete&rederive | ||
197 | // phases | ||
198 | // aggregator and transitive closure parent nodes also generate excess updates that should be | ||
199 | // cancelled | ||
200 | directParentLoop: for (Node directParent : directParents) { | ||
201 | Set<Node> parentsToCheck = new HashSet<>(); | ||
202 | // check the case where a direct parent is the reason for mailbox usage | ||
203 | parentsToCheck.add(directParent); | ||
204 | // check the case where an indirect parent (join slot) is the reason for mailbox usage | ||
205 | if (directParent instanceof DualInputNode) { | ||
206 | // in case of existence join (typically antijoin), a mailbox should allow | ||
207 | // an insertion and deletion (at the secondary slot) to cancel each other out | ||
208 | if (directParent instanceof ExistenceNode) { | ||
209 | fallThrough = false; | ||
210 | break directParentLoop; | ||
211 | } | ||
212 | // in beta nodes, indexer slots (or their active nodes) are considered indirect parents | ||
213 | DualInputNode dualInput = (DualInputNode) directParent; | ||
214 | IterableIndexer primarySlot = dualInput.getPrimarySlot(); | ||
215 | if (primarySlot != null) | ||
216 | parentsToCheck.add(primarySlot.getActiveNode()); | ||
217 | Indexer secondarySlot = dualInput.getSecondarySlot(); | ||
218 | if (secondarySlot != null) | ||
219 | parentsToCheck.add(secondarySlot.getActiveNode()); | ||
220 | } | ||
221 | for (Node parent : parentsToCheck) { | ||
222 | CommunicationGroup parentGroup = groupMap.get(parent); | ||
223 | if ( // parent is in a different, recursive group | ||
224 | (group != parentGroup && parentGroup.isRecursive()) || | ||
225 | // node and parent within the same recursive group, and... | ||
226 | (group == parentGroup && group.isRecursive() && ( | ||
227 | // parent is a transitive closure or aggregator node, or a trimmer | ||
228 | // allow trimmed or disjunctive tuple updates to cancel each other | ||
229 | (parent instanceof TransitiveClosureNode) || (parent instanceof IAggregatorNode) | ||
230 | || trueTrimming(parent)))) { | ||
231 | fallThrough = false; | ||
232 | break directParentLoop; | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | // overwrite fallthrough flag with newly computed value | ||
238 | ((FallThroughCapableMailbox) mailbox).setFallThrough(fallThrough); | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * A trimmer node that actually eliminates some columns (not just reorders) | ||
245 | */ | ||
246 | private boolean trueTrimming(Node node) { | ||
247 | if (node instanceof TrimmerNode) { | ||
248 | TupleMask mask = ((TrimmerNode) node).getMask(); | ||
249 | return (mask.indices.length != mask.sourceWidth); | ||
250 | } | ||
251 | return false; | ||
252 | } | ||
253 | |||
254 | public void activateUnenqueued(final CommunicationGroup group) { | ||
255 | groupQueue.add(group); | ||
256 | group.isEnqueued = true; | ||
257 | } | ||
258 | |||
259 | public void deactivate(final CommunicationGroup group) { | ||
260 | groupQueue.remove(group); | ||
261 | group.isEnqueued = false; | ||
262 | } | ||
263 | |||
264 | public CommunicationGroup getAndRemoveFirstGroup() { | ||
265 | final CommunicationGroup group = groupQueue.poll(); | ||
266 | group.isEnqueued = false; | ||
267 | return group; | ||
268 | } | ||
269 | |||
270 | public boolean isEmpty() { | ||
271 | return groupQueue.isEmpty(); | ||
272 | } | ||
273 | |||
274 | protected abstract CommunicationGroup createGroup(final Node representative, final int index); | ||
275 | |||
276 | protected CommunicationGroup createAndStoreGroup(final Node representative, final int index) { | ||
277 | final CommunicationGroup group = createGroup(representative, index); | ||
278 | addToGroup(representative, group); | ||
279 | return group; | ||
280 | } | ||
281 | |||
282 | /** | ||
283 | * Registers the dependency that the target {@link Node} depends on the source {@link Node}. In other words, source | ||
284 | * may send messages to target in the RETE network. If the dependency edge is already present, this method call is a | ||
285 | * noop. | ||
286 | * | ||
287 | * @param source | ||
288 | * the source node | ||
289 | * @param target | ||
290 | * the target node | ||
291 | */ | ||
292 | public void registerDependency(final Node source, final Node target) { | ||
293 | // nodes can be immediately inserted, if they already exist in the graph, this is a noop | ||
294 | dependencyGraph.insertNode(source); | ||
295 | dependencyGraph.insertNode(target); | ||
296 | |||
297 | if (!this.dependencyGraph.getTargetNodes(source).containsNonZero(target)) { | ||
298 | |||
299 | // query all these information before the actual edge insertion | ||
300 | // because SCCs may be unified during the process | ||
301 | final Node sourceRepresentative = sccInformationProvider.getRepresentative(source); | ||
302 | final Node targetRepresentative = sccInformationProvider.getRepresentative(target); | ||
303 | final boolean targetHadOutgoingEdges = sccInformationProvider.hasOutgoingEdges(targetRepresentative); | ||
304 | |||
305 | // insert the edge | ||
306 | dependencyGraph.insertEdge(source, target); | ||
307 | |||
308 | // create groups if they do not yet exist | ||
309 | CommunicationGroup sourceGroup = groupMap.get(sourceRepresentative); | ||
310 | if (sourceGroup == null) { | ||
311 | // create on-demand with the next smaller group id | ||
312 | sourceGroup = createAndStoreGroup(sourceRepresentative, --minGroupId); | ||
313 | } | ||
314 | final int sourceIndex = sourceGroup.identifier; | ||
315 | |||
316 | CommunicationGroup targetGroup = groupMap.get(targetRepresentative); | ||
317 | if (targetGroup == null) { | ||
318 | // create on-demand with the next larger group id | ||
319 | targetGroup = createAndStoreGroup(targetRepresentative, ++maxGroupId); | ||
320 | } | ||
321 | final int targetIndex = targetGroup.identifier; | ||
322 | |||
323 | if (sourceIndex <= targetIndex) { | ||
324 | // indices obey current topological ordering | ||
325 | refreshFallThroughFlag(target); | ||
326 | postProcessNode(source); | ||
327 | postProcessNode(target); | ||
328 | postProcessGroup(sourceGroup); | ||
329 | if (sourceGroup != targetGroup) { | ||
330 | postProcessGroup(targetGroup); | ||
331 | } | ||
332 | } else if (sourceIndex > targetIndex && !targetHadOutgoingEdges) { | ||
333 | // indices violate current topological ordering, but we can simply bump the target index | ||
334 | final boolean wasEnqueued = targetGroup.isEnqueued; | ||
335 | if (wasEnqueued) { | ||
336 | groupQueue.remove(targetGroup); | ||
337 | } | ||
338 | targetGroup.identifier = ++maxGroupId; | ||
339 | if (wasEnqueued) { | ||
340 | groupQueue.add(targetGroup); | ||
341 | } | ||
342 | |||
343 | refreshFallThroughFlag(target); | ||
344 | postProcessNode(source); | ||
345 | postProcessNode(target); | ||
346 | postProcessGroup(sourceGroup); | ||
347 | postProcessGroup(targetGroup); | ||
348 | } else { | ||
349 | // needs a full re-computation because of more complex change | ||
350 | precomputeGroups(); | ||
351 | } | ||
352 | } | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * Returns true if the given {@link Node} is in a recursive {@link CommunicationGroup}, false otherwise. | ||
357 | */ | ||
358 | public boolean isInRecursiveGroup(final Node node) { | ||
359 | final CommunicationGroup group = this.getGroup(node); | ||
360 | if (group == null) { | ||
361 | return false; | ||
362 | } else { | ||
363 | return group.isRecursive(); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | /** | ||
368 | * Returns true if the given two {@link Node}s are in the same {@link CommunicationGroup}. | ||
369 | */ | ||
370 | public boolean areInSameGroup(final Node left, final Node right) { | ||
371 | final CommunicationGroup leftGroup = this.getGroup(left); | ||
372 | final CommunicationGroup rightGroup = this.getGroup(right); | ||
373 | return leftGroup != null && leftGroup == rightGroup; | ||
374 | } | ||
375 | |||
376 | /** | ||
377 | * Unregisters a dependency between source and target. | ||
378 | * | ||
379 | * @param source | ||
380 | * the source node | ||
381 | * @param target | ||
382 | * the target node | ||
383 | */ | ||
384 | public void unregisterDependency(final Node source, final Node target) { | ||
385 | // delete the edge first, and then query the SCC info provider | ||
386 | this.dependencyGraph.deleteEdgeIfExists(source, target); | ||
387 | |||
388 | final Node sourceRepresentative = sccInformationProvider.getRepresentative(source); | ||
389 | final Node targetRepresentative = sccInformationProvider.getRepresentative(target); | ||
390 | |||
391 | // if they are still in the same SCC, | ||
392 | // then this deletion did not affect the SCCs, | ||
393 | // and it is sufficient to recompute affected fall-through flags; | ||
394 | // otherwise, we need a new pre-computation for the groupMap and groupQueue | ||
395 | if (sourceRepresentative.equals(targetRepresentative)) { | ||
396 | // this deletion could not have affected the split flags | ||
397 | refreshFallThroughFlag(target); | ||
398 | postProcessNode(source); | ||
399 | postProcessNode(target); | ||
400 | } else { | ||
401 | // preComputeGroups takes care of the split flag maintenance | ||
402 | precomputeGroups(); | ||
403 | } | ||
404 | } | ||
405 | |||
406 | /** | ||
407 | * Refresh fall-through flags if dependencies change for given target, but no SCC change | ||
408 | */ | ||
409 | private void refreshFallThroughFlag(final Node target) { | ||
410 | precomputeFallThroughFlag(target); | ||
411 | if (target instanceof DualInputNode) { | ||
412 | for (final Node indirectTarget : dependencyGraph.getTargetNodes(target).distinctValues()) { | ||
413 | precomputeFallThroughFlag(indirectTarget); | ||
414 | } | ||
415 | } | ||
416 | } | ||
417 | |||
418 | /** | ||
419 | * Returns true if the given source-target edge in the communication network acts as a recursion cut point. | ||
420 | * The current implementation considers edges leading into {@link ProductionNode}s as cut point iff | ||
421 | * both source and target belong to the same group. | ||
422 | * | ||
423 | * @param source the source node | ||
424 | * @param target the target node | ||
425 | * @return true if the edge is a cut point, false otherwise | ||
426 | * @since 2.4 | ||
427 | */ | ||
428 | protected boolean isRecursionCutPoint(final Node source, final Node target) { | ||
429 | final Node effectiveSource = source instanceof SpecializedProjectionIndexer | ||
430 | ? ((SpecializedProjectionIndexer) source).getActiveNode() | ||
431 | : source; | ||
432 | final CommunicationGroup sourceGroup = this.getGroup(effectiveSource); | ||
433 | final CommunicationGroup targetGroup = this.getGroup(target); | ||
434 | return sourceGroup != null && sourceGroup == targetGroup && target instanceof ProductionNode; | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * This hook allows concrete tracker implementations to perform tracker-specific post processing on nodes (cf. | ||
439 | * {@link NetworkStructureChangeSensitiveNode} and {@link BehaviorChangingMailbox}). At the time of the invocation, | ||
440 | * the network topology has already been updated. | ||
441 | */ | ||
442 | protected abstract void postProcessNode(final Node node); | ||
443 | |||
444 | /** | ||
445 | * This hook allows concrete tracker implementations to perform tracker-specific post processing on groups. At the | ||
446 | * time of the invocation, the network topology has already been updated. | ||
447 | * @since 2.4 | ||
448 | */ | ||
449 | protected abstract void postProcessGroup(final CommunicationGroup group); | ||
450 | |||
451 | /** | ||
452 | * Creates a proxy for the given {@link Mailbox} for the given requester {@link Node}. The proxy creation is | ||
453 | * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used | ||
454 | * to create {@link TimelyMailboxProxy}s depending on the network topology. There is no guarantee that the same | ||
455 | * proxy instance is returned when this method is called multiple times with the same arguments. | ||
456 | */ | ||
457 | public abstract Mailbox proxifyMailbox(final Node requester, final Mailbox original); | ||
458 | |||
459 | /** | ||
460 | * Creates a proxy for the given {@link IndexerListener} for the given requester {@link Node}. The proxy creation is | ||
461 | * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used | ||
462 | * to create {@link TimelyIndexerListenerProxy}s depending on the network topology. There is no guarantee that the | ||
463 | * same proxy instance is returned when this method is called multiple times with the same arguments. | ||
464 | */ | ||
465 | public abstract IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original); | ||
466 | |||
467 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java new file mode 100644 index 00000000..e1a61693 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java | |||
@@ -0,0 +1,19 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.communication; | ||
10 | |||
11 | /** | ||
12 | * Subclasses of this interface represent meta data of update messages in Rete. | ||
13 | * | ||
14 | * @author Tamas Szabo | ||
15 | * @since 2.3 | ||
16 | */ | ||
17 | public interface MessageSelector { | ||
18 | |||
19 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java new file mode 100644 index 00000000..27779352 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java | |||
@@ -0,0 +1,32 @@ | |||
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.rete.network.communication; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
15 | |||
16 | /** | ||
17 | * @since 2.4 | ||
18 | */ | ||
19 | public class NodeComparator implements Comparator<Node> { | ||
20 | |||
21 | protected final Map<Node, Integer> nodeMap; | ||
22 | |||
23 | public NodeComparator(final Map<Node, Integer> nodeMap) { | ||
24 | this.nodeMap = nodeMap; | ||
25 | } | ||
26 | |||
27 | @Override | ||
28 | public int compare(final Node left, final Node right) { | ||
29 | return this.nodeMap.get(left) - this.nodeMap.get(right); | ||
30 | } | ||
31 | |||
32 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java new file mode 100644 index 00000000..41cd8cd3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java | |||
@@ -0,0 +1,34 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, 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.rete.network.communication; | ||
10 | |||
11 | /** | ||
12 | * A default message selector that can be used to associate phases to messages. | ||
13 | * | ||
14 | * @author Tamas Szabo | ||
15 | * @since 2.3 | ||
16 | */ | ||
17 | public enum PhasedSelector implements MessageSelector { | ||
18 | |||
19 | /** | ||
20 | * No special distinguishing feature | ||
21 | */ | ||
22 | DEFAULT, | ||
23 | |||
24 | /** | ||
25 | * Inserts and delete-insert monotone change pairs | ||
26 | */ | ||
27 | MONOTONE, | ||
28 | |||
29 | /** | ||
30 | * Deletes | ||
31 | */ | ||
32 | ANTI_MONOTONE | ||
33 | |||
34 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java new file mode 100644 index 00000000..a50a63a8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java | |||
@@ -0,0 +1,124 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.communication; | ||
10 | |||
11 | import java.util.AbstractMap; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; | ||
18 | |||
19 | /** | ||
20 | * A timestamp associated with update messages in timely evaluation. | ||
21 | * | ||
22 | * @author Tamas Szabo | ||
23 | * @since 2.3 | ||
24 | */ | ||
25 | public class Timestamp implements Comparable<Timestamp>, MessageSelector { | ||
26 | |||
27 | protected final int value; | ||
28 | public static final Timestamp ZERO = new Timestamp(0); | ||
29 | /** | ||
30 | * @since 2.4 | ||
31 | */ | ||
32 | public static final Timeline<Timestamp> INSERT_AT_ZERO_TIMELINE = Timelines.createFrom(Timestamp.ZERO); | ||
33 | |||
34 | public Timestamp(final int value) { | ||
35 | this.value = value; | ||
36 | } | ||
37 | |||
38 | public int getValue() { | ||
39 | return value; | ||
40 | } | ||
41 | |||
42 | public Timestamp max(final Timestamp that) { | ||
43 | if (this.value >= that.value) { | ||
44 | return this; | ||
45 | } else { | ||
46 | return that; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * @since 2.4 | ||
52 | */ | ||
53 | public Timestamp min(final Timestamp that) { | ||
54 | if (this.value <= that.value) { | ||
55 | return this; | ||
56 | } else { | ||
57 | return that; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public int compareTo(final Timestamp that) { | ||
63 | return this.value - that.value; | ||
64 | } | ||
65 | |||
66 | @Override | ||
67 | public boolean equals(final Object obj) { | ||
68 | if (obj == null || !(obj instanceof Timestamp)) { | ||
69 | return false; | ||
70 | } else { | ||
71 | return this.value == ((Timestamp) obj).value; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public int hashCode() { | ||
77 | return this.value; | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public String toString() { | ||
82 | return Integer.toString(this.value); | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * A {@link Map} implementation that associates the zero timestamp with every key. There is no suppor for | ||
87 | * {@link Map#entrySet()} due to performance reasons. | ||
88 | * | ||
89 | * @author Tamas Szabo | ||
90 | */ | ||
91 | public static final class AllZeroMap<T> extends AbstractMap<T, Timeline<Timestamp>> { | ||
92 | |||
93 | private final Collection<T> wrapped; | ||
94 | |||
95 | public AllZeroMap(Set<T> wrapped) { | ||
96 | this.wrapped = wrapped; | ||
97 | } | ||
98 | |||
99 | @Override | ||
100 | public Set<Entry<T, Timeline<Timestamp>>> entrySet() { | ||
101 | throw new UnsupportedOperationException("Use the combination of keySet() and get()!"); | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * @since 2.4 | ||
106 | */ | ||
107 | @Override | ||
108 | public Timeline<Timestamp> get(final Object key) { | ||
109 | return INSERT_AT_ZERO_TIMELINE; | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public Set<T> keySet() { | ||
114 | return (Set<T>) this.wrapped; | ||
115 | } | ||
116 | |||
117 | @Override | ||
118 | public String toString() { | ||
119 | return this.getClass().getSimpleName() + ": " + this.keySet().toString(); | ||
120 | } | ||
121 | |||
122 | } | ||
123 | |||
124 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java new file mode 100644 index 00000000..d8260384 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java | |||
@@ -0,0 +1,164 @@ | |||
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.rete.network.communication.timeless; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.EnumMap; | ||
14 | import java.util.LinkedHashSet; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
20 | import tools.refinery.viatra.runtime.rete.network.RederivableNode; | ||
21 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; | ||
25 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
26 | |||
27 | /** | ||
28 | * A communication group representing either a single node where the | ||
29 | * node is a monotonicity aware one or a set of nodes that form an SCC. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * @since 2.4 | ||
33 | */ | ||
34 | public class RecursiveCommunicationGroup extends CommunicationGroup { | ||
35 | |||
36 | private final Set<Mailbox> antiMonotoneMailboxes; | ||
37 | private final Set<Mailbox> monotoneMailboxes; | ||
38 | private final Set<Mailbox> defaultMailboxes; | ||
39 | private final Set<RederivableNode> rederivables; | ||
40 | private boolean currentlyDelivering; | ||
41 | |||
42 | /** | ||
43 | * @since 1.7 | ||
44 | */ | ||
45 | public RecursiveCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) { | ||
46 | super(tracker, representative, identifier); | ||
47 | this.antiMonotoneMailboxes = CollectionsFactory.createSet(); | ||
48 | this.monotoneMailboxes = CollectionsFactory.createSet(); | ||
49 | this.defaultMailboxes = CollectionsFactory.createSet(); | ||
50 | this.rederivables = new LinkedHashSet<RederivableNode>(); | ||
51 | this.currentlyDelivering = false; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public void deliverMessages() { | ||
56 | this.currentlyDelivering = true; | ||
57 | |||
58 | // ANTI-MONOTONE PHASE | ||
59 | while (!this.antiMonotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) { | ||
60 | while (!this.antiMonotoneMailboxes.isEmpty()) { | ||
61 | final Mailbox mailbox = this.antiMonotoneMailboxes.iterator().next(); | ||
62 | this.antiMonotoneMailboxes.remove(mailbox); | ||
63 | mailbox.deliverAll(PhasedSelector.ANTI_MONOTONE); | ||
64 | } | ||
65 | while (!this.defaultMailboxes.isEmpty()) { | ||
66 | final Mailbox mailbox = this.defaultMailboxes.iterator().next(); | ||
67 | this.defaultMailboxes.remove(mailbox); | ||
68 | mailbox.deliverAll(PhasedSelector.DEFAULT); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | // REDERIVE PHASE | ||
73 | while (!this.rederivables.isEmpty()) { | ||
74 | // re-derivable nodes take care of their unregistration!! | ||
75 | final RederivableNode node = this.rederivables.iterator().next(); | ||
76 | node.rederiveOne(); | ||
77 | } | ||
78 | |||
79 | // MONOTONE PHASE | ||
80 | while (!this.monotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) { | ||
81 | while (!this.monotoneMailboxes.isEmpty()) { | ||
82 | final Mailbox mailbox = this.monotoneMailboxes.iterator().next(); | ||
83 | this.monotoneMailboxes.remove(mailbox); | ||
84 | mailbox.deliverAll(PhasedSelector.MONOTONE); | ||
85 | } | ||
86 | while (!this.defaultMailboxes.isEmpty()) { | ||
87 | final Mailbox mailbox = this.defaultMailboxes.iterator().next(); | ||
88 | this.defaultMailboxes.remove(mailbox); | ||
89 | mailbox.deliverAll(PhasedSelector.DEFAULT); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | this.currentlyDelivering = false; | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public boolean isEmpty() { | ||
98 | return this.rederivables.isEmpty() && this.antiMonotoneMailboxes.isEmpty() | ||
99 | && this.monotoneMailboxes.isEmpty() && this.defaultMailboxes.isEmpty(); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) { | ||
104 | final Collection<Mailbox> mailboxes = getMailboxContainer(kind); | ||
105 | mailboxes.add(mailbox); | ||
106 | if (!this.isEnqueued && !this.currentlyDelivering) { | ||
107 | this.tracker.activateUnenqueued(this); | ||
108 | } | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) { | ||
113 | final Collection<Mailbox> mailboxes = getMailboxContainer(kind); | ||
114 | mailboxes.remove(mailbox); | ||
115 | if (isEmpty()) { | ||
116 | this.tracker.deactivate(this); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | private Collection<Mailbox> getMailboxContainer(final MessageSelector kind) { | ||
121 | if (kind == PhasedSelector.ANTI_MONOTONE) { | ||
122 | return this.antiMonotoneMailboxes; | ||
123 | } else if (kind == PhasedSelector.MONOTONE) { | ||
124 | return this.monotoneMailboxes; | ||
125 | } else if (kind == PhasedSelector.DEFAULT) { | ||
126 | return this.defaultMailboxes; | ||
127 | } else { | ||
128 | throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | public void addRederivable(final RederivableNode node) { | ||
133 | this.rederivables.add(node); | ||
134 | if (!this.isEnqueued) { | ||
135 | this.tracker.activateUnenqueued(this); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | public void removeRederivable(final RederivableNode node) { | ||
140 | this.rederivables.remove(node); | ||
141 | if (isEmpty()) { | ||
142 | this.tracker.deactivate(this); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | public Collection<RederivableNode> getRederivables() { | ||
147 | return this.rederivables; | ||
148 | } | ||
149 | |||
150 | @Override | ||
151 | public Map<MessageSelector, Collection<Mailbox>> getMailboxes() { | ||
152 | Map<PhasedSelector, Collection<Mailbox>> map = new EnumMap<>(PhasedSelector.class); | ||
153 | map.put(PhasedSelector.ANTI_MONOTONE, antiMonotoneMailboxes); | ||
154 | map.put(PhasedSelector.MONOTONE, monotoneMailboxes); | ||
155 | map.put(PhasedSelector.DEFAULT, defaultMailboxes); | ||
156 | return Collections.unmodifiableMap(map); | ||
157 | } | ||
158 | |||
159 | @Override | ||
160 | public boolean isRecursive() { | ||
161 | return true; | ||
162 | } | ||
163 | |||
164 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java new file mode 100644 index 00000000..c51c7dbf --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java | |||
@@ -0,0 +1,86 @@ | |||
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.rete.network.communication.timeless; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
16 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
17 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
18 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; | ||
20 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
21 | |||
22 | /** | ||
23 | * A communication group containing only a single node with a single default | ||
24 | * mailbox. | ||
25 | * | ||
26 | * @author Tamas Szabo | ||
27 | * @since 1.6 | ||
28 | */ | ||
29 | public class SingletonCommunicationGroup extends CommunicationGroup { | ||
30 | |||
31 | private Mailbox mailbox; | ||
32 | |||
33 | /** | ||
34 | * @since 1.7 | ||
35 | */ | ||
36 | public SingletonCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) { | ||
37 | super(tracker, representative, identifier); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public void deliverMessages() { | ||
42 | this.mailbox.deliverAll(PhasedSelector.DEFAULT); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public boolean isEmpty() { | ||
47 | return this.mailbox == null; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) { | ||
52 | if (kind == PhasedSelector.DEFAULT) { | ||
53 | this.mailbox = mailbox; | ||
54 | if (!this.isEnqueued) { | ||
55 | this.tracker.activateUnenqueued(this); | ||
56 | } | ||
57 | } else { | ||
58 | throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) { | ||
64 | if (kind == PhasedSelector.DEFAULT) { | ||
65 | this.mailbox = null; | ||
66 | this.tracker.deactivate(this); | ||
67 | } else { | ||
68 | throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public Map<MessageSelector, Collection<Mailbox>> getMailboxes() { | ||
74 | if (mailbox != null) { | ||
75 | return Collections.singletonMap(PhasedSelector.DEFAULT, Collections.singleton(mailbox)); | ||
76 | } else { | ||
77 | return Collections.emptyMap(); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public boolean isRecursive() { | ||
83 | return false; | ||
84 | } | ||
85 | |||
86 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java new file mode 100644 index 00000000..1c18c1cd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java | |||
@@ -0,0 +1,149 @@ | |||
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.rete.network.communication.timeless; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.rete.index.DualInputNode; | ||
17 | import tools.refinery.viatra.runtime.rete.index.Indexer; | ||
18 | import tools.refinery.viatra.runtime.rete.index.IndexerListener; | ||
19 | import tools.refinery.viatra.runtime.rete.index.IterableIndexer; | ||
20 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
21 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
22 | import tools.refinery.viatra.runtime.rete.network.RederivableNode; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
26 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
27 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
28 | |||
29 | /** | ||
30 | * Timeless implementation of the communication tracker. | ||
31 | * | ||
32 | * @author Tamas Szabo | ||
33 | * @since 2.2 | ||
34 | */ | ||
35 | public class TimelessCommunicationTracker extends CommunicationTracker { | ||
36 | |||
37 | @Override | ||
38 | protected CommunicationGroup createGroup(Node representative, int index) { | ||
39 | final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1; | ||
40 | final boolean isReceiver = representative instanceof Receiver; | ||
41 | final boolean isPosetIndifferent = isReceiver | ||
42 | && ((Receiver) representative).getMailbox() instanceof BehaviorChangingMailbox; | ||
43 | final boolean isSingletonInDRedMode = isSingleton && (representative instanceof RederivableNode) | ||
44 | && ((RederivableNode) representative).isInDRedMode(); | ||
45 | |||
46 | CommunicationGroup group = null; | ||
47 | // we can only use a singleton group iff | ||
48 | // (1) the SCC has one node AND | ||
49 | // (2) either we have a poset-indifferent mailbox OR the node is not even a receiver AND | ||
50 | // (3) the node does not run in DRed mode in a singleton group | ||
51 | if (isSingleton && (isPosetIndifferent || !isReceiver) && !isSingletonInDRedMode) { | ||
52 | group = new SingletonCommunicationGroup(this, representative, index); | ||
53 | } else { | ||
54 | group = new RecursiveCommunicationGroup(this, representative, index); | ||
55 | } | ||
56 | |||
57 | return group; | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | protected void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups) { | ||
62 | for (final CommunicationGroup oldGroup : oldActiveGroups) { | ||
63 | for (final Entry<MessageSelector, Collection<Mailbox>> entry : oldGroup.getMailboxes().entrySet()) { | ||
64 | for (final Mailbox mailbox : entry.getValue()) { | ||
65 | final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver()); | ||
66 | newGroup.notifyHasMessage(mailbox, entry.getKey()); | ||
67 | } | ||
68 | } | ||
69 | |||
70 | if (oldGroup instanceof RecursiveCommunicationGroup) { | ||
71 | for (final RederivableNode node : ((RecursiveCommunicationGroup) oldGroup).getRederivables()) { | ||
72 | final CommunicationGroup newGroup = this.groupMap.get(node); | ||
73 | if (!(newGroup instanceof RecursiveCommunicationGroup)) { | ||
74 | throw new IllegalStateException("The new group must also be recursive! " + newGroup); | ||
75 | } | ||
76 | ((RecursiveCommunicationGroup) newGroup).addRederivable(node); | ||
77 | } | ||
78 | } | ||
79 | } | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public Mailbox proxifyMailbox(final Node requester, final Mailbox original) { | ||
84 | return original; | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) { | ||
89 | return original; | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | protected void postProcessNode(final Node node) { | ||
94 | if (node instanceof Receiver) { | ||
95 | final Mailbox mailbox = ((Receiver) node).getMailbox(); | ||
96 | if (mailbox instanceof BehaviorChangingMailbox) { | ||
97 | final CommunicationGroup group = this.groupMap.get(node); | ||
98 | final Set<Node> sccNodes = this.sccInformationProvider.sccs.getPartition(node); | ||
99 | // a default mailbox must split its messages iff | ||
100 | // (1) its receiver is in a recursive group and | ||
101 | final boolean c1 = group.isRecursive(); | ||
102 | // (2) its receiver is at the SCC boundary of that group | ||
103 | final boolean c2 = isAtSCCBoundary(node); | ||
104 | // (3) its group consists of more than one node | ||
105 | final boolean c3 = sccNodes.size() > 1; | ||
106 | ((BehaviorChangingMailbox) mailbox).setSplitFlag(c1 && c2 && c3); | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | protected void postProcessGroup(final CommunicationGroup group) { | ||
113 | |||
114 | } | ||
115 | |||
116 | /** | ||
117 | * @since 2.0 | ||
118 | */ | ||
119 | private boolean isAtSCCBoundary(final Node node) { | ||
120 | final CommunicationGroup ownGroup = this.groupMap.get(node); | ||
121 | assert ownGroup != null; | ||
122 | for (final Node source : this.dependencyGraph.getSourceNodes(node).distinctValues()) { | ||
123 | final Set<Node> sourcesToCheck = new HashSet<Node>(); | ||
124 | sourcesToCheck.add(source); | ||
125 | // DualInputNodes must be checked additionally because they do not use a mailbox directly. | ||
126 | // It can happen that their indexers actually belong to other SCCs. | ||
127 | if (source instanceof DualInputNode) { | ||
128 | final DualInputNode dualInput = (DualInputNode) source; | ||
129 | final IterableIndexer primarySlot = dualInput.getPrimarySlot(); | ||
130 | if (primarySlot != null) { | ||
131 | sourcesToCheck.add(primarySlot.getActiveNode()); | ||
132 | } | ||
133 | final Indexer secondarySlot = dualInput.getSecondarySlot(); | ||
134 | if (secondarySlot != null) { | ||
135 | sourcesToCheck.add(secondarySlot.getActiveNode()); | ||
136 | } | ||
137 | } | ||
138 | for (final Node current : sourcesToCheck) { | ||
139 | final CommunicationGroup otherGroup = this.groupMap.get(current); | ||
140 | assert otherGroup != null; | ||
141 | if (!ownGroup.equals(otherGroup)) { | ||
142 | return true; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | return false; | ||
147 | } | ||
148 | |||
149 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java new file mode 100644 index 00000000..8097bd91 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.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.rete.network.communication.timely; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.IGroupable; | ||
12 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
13 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
14 | |||
15 | /** | ||
16 | * {@link Node}s that implement this interface can resume folding of their states when instructed during timely evaluation. | ||
17 | * | ||
18 | * @since 2.3 | ||
19 | * @author Tamas Szabo | ||
20 | */ | ||
21 | public interface ResumableNode extends Node, IGroupable { | ||
22 | |||
23 | /** | ||
24 | * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to | ||
25 | * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more | ||
26 | * folding to do towards higher timestamps. | ||
27 | */ | ||
28 | public void resumeAt(final Timestamp timestamp); | ||
29 | |||
30 | /** | ||
31 | * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this | ||
32 | * resumable. | ||
33 | */ | ||
34 | public Timestamp getResumableTimestamp(); | ||
35 | |||
36 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java new file mode 100644 index 00000000..0394d92c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java | |||
@@ -0,0 +1,171 @@ | |||
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.rete.network.communication.timely; | ||
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.Map.Entry; | ||
17 | import java.util.Set; | ||
18 | import java.util.TreeMap; | ||
19 | import java.util.TreeSet; | ||
20 | |||
21 | import org.apache.log4j.Logger; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
23 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
26 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
27 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
28 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
29 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
30 | |||
31 | /** | ||
32 | * A timely communication group implementation. {@link TimelyMailbox}es and {@link LazyFoldingNode}s are ordered in the | ||
33 | * increasing order of timestamps. | ||
34 | * | ||
35 | * @author Tamas Szabo | ||
36 | * @since 2.3 | ||
37 | */ | ||
38 | public class TimelyCommunicationGroup extends CommunicationGroup { | ||
39 | |||
40 | private final boolean isSingleton; | ||
41 | private final TreeMap<Timestamp, Set<Mailbox>> mailboxQueue; | ||
42 | // may be null - only used in the scattered case where we need to take care of mailboxes and resumables too | ||
43 | private Comparator<Node> nodeComparator; | ||
44 | private boolean currentlyDelivering; | ||
45 | private Timestamp currentlyDeliveredTimestamp; | ||
46 | |||
47 | public TimelyCommunicationGroup(final TimelyCommunicationTracker tracker, final Node representative, | ||
48 | final int identifier, final boolean isSingleton) { | ||
49 | super(tracker, representative, identifier); | ||
50 | this.isSingleton = isSingleton; | ||
51 | this.mailboxQueue = CollectionsFactory.createTreeMap(); | ||
52 | this.currentlyDelivering = false; | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * Sets the {@link Comparator} to be used to order the {@link Mailbox}es at a given {@link Timestamp} in the mailbox | ||
57 | * queue. Additionally, reorders already queued {@link Mailbox}es to reflect the new comparator. The comparator may | ||
58 | * be null, in this case, no set ordering will be enforced among the {@link Mailbox}es. | ||
59 | */ | ||
60 | public void setComparatorAndReorderMailboxes(final Comparator<Node> nodeComparator) { | ||
61 | this.nodeComparator = nodeComparator; | ||
62 | if (!this.mailboxQueue.isEmpty()) { | ||
63 | final HashMap<Timestamp, Set<Mailbox>> queueCopy = new HashMap<Timestamp, Set<Mailbox>>(this.mailboxQueue); | ||
64 | this.mailboxQueue.clear(); | ||
65 | for (final Entry<Timestamp, Set<Mailbox>> entry : queueCopy.entrySet()) { | ||
66 | for (final Mailbox mailbox : entry.getValue()) { | ||
67 | this.notifyHasMessage(mailbox, entry.getKey()); | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public void deliverMessages() { | ||
75 | this.currentlyDelivering = true; | ||
76 | while (!this.mailboxQueue.isEmpty()) { | ||
77 | // care must be taken here how we iterate over the mailboxes | ||
78 | // it is not okay to loop over the mailboxes at once because a mailbox may disappear from the collection as | ||
79 | // a result of delivering messages from another mailboxes under the same timestamp | ||
80 | // because of this, it is crucial that we pick the mailboxes one by one | ||
81 | final Entry<Timestamp, Set<Mailbox>> entry = this.mailboxQueue.firstEntry(); | ||
82 | final Timestamp timestamp = entry.getKey(); | ||
83 | final Set<Mailbox> mailboxes = entry.getValue(); | ||
84 | final Mailbox mailbox = mailboxes.iterator().next(); | ||
85 | mailboxes.remove(mailbox); | ||
86 | if (mailboxes.isEmpty()) { | ||
87 | this.mailboxQueue.pollFirstEntry(); | ||
88 | } | ||
89 | assert mailbox instanceof TimelyMailbox; | ||
90 | /* debug */ this.currentlyDeliveredTimestamp = timestamp; | ||
91 | mailbox.deliverAll(timestamp); | ||
92 | /* debug */ this.currentlyDeliveredTimestamp = null; | ||
93 | } | ||
94 | this.currentlyDelivering = false; | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public boolean isEmpty() { | ||
99 | return this.mailboxQueue.isEmpty(); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public void notifyHasMessage(final Mailbox mailbox, MessageSelector kind) { | ||
104 | if (kind instanceof Timestamp) { | ||
105 | final Timestamp timestamp = (Timestamp) kind; | ||
106 | if (Options.MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS) { | ||
107 | if (timestamp.compareTo(this.currentlyDeliveredTimestamp) < 0) { | ||
108 | final Logger logger = this.representative.getContainer().getNetwork().getEngine().getLogger(); | ||
109 | logger.error( | ||
110 | "[INTERNAL ERROR] Violation of differential dataflow communication schema! The communication component with representative " | ||
111 | + this.representative + " observed decreasing timestamp during message delivery!"); | ||
112 | } | ||
113 | } | ||
114 | final Set<Mailbox> mailboxes = this.mailboxQueue.computeIfAbsent(timestamp, k -> { | ||
115 | if (this.nodeComparator == null) { | ||
116 | return CollectionsFactory.createSet(); | ||
117 | } else { | ||
118 | return new TreeSet<Mailbox>(new Comparator<Mailbox>() { | ||
119 | @Override | ||
120 | public int compare(final Mailbox left, final Mailbox right) { | ||
121 | return nodeComparator.compare(left.getReceiver(), right.getReceiver()); | ||
122 | } | ||
123 | }); | ||
124 | } | ||
125 | }); | ||
126 | mailboxes.add(mailbox); | ||
127 | if (!this.isEnqueued && !this.currentlyDelivering) { | ||
128 | this.tracker.activateUnenqueued(this); | ||
129 | } | ||
130 | } else { | ||
131 | throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) { | ||
137 | if (kind instanceof Timestamp) { | ||
138 | final Timestamp timestamp = (Timestamp) kind; | ||
139 | this.mailboxQueue.compute(timestamp, (k, v) -> { | ||
140 | if (v == null) { | ||
141 | throw new IllegalStateException("No mailboxes registered at timestamp " + timestamp + "!"); | ||
142 | } | ||
143 | if (!v.remove(mailbox)) { | ||
144 | throw new IllegalStateException( | ||
145 | "The mailbox " + mailbox + " was not registered at timestamp " + timestamp + "!"); | ||
146 | } | ||
147 | if (v.isEmpty()) { | ||
148 | return null; | ||
149 | } else { | ||
150 | return v; | ||
151 | } | ||
152 | }); | ||
153 | if (this.mailboxQueue.isEmpty()) { | ||
154 | this.tracker.deactivate(this); | ||
155 | } | ||
156 | } else { | ||
157 | throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | @Override | ||
162 | public Map<MessageSelector, Collection<Mailbox>> getMailboxes() { | ||
163 | return Collections.unmodifiableMap(this.mailboxQueue); | ||
164 | } | ||
165 | |||
166 | @Override | ||
167 | public boolean isRecursive() { | ||
168 | return !this.isSingleton; | ||
169 | } | ||
170 | |||
171 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java new file mode 100644 index 00000000..79179880 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java | |||
@@ -0,0 +1,216 @@ | |||
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.rete.network.communication.timely; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | import java.util.Set; | ||
16 | import java.util.function.Function; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting; | ||
19 | import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
21 | import tools.refinery.viatra.runtime.rete.index.IndexerListener; | ||
22 | import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer; | ||
23 | import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription; | ||
24 | import tools.refinery.viatra.runtime.rete.index.StandardIndexer; | ||
25 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; | ||
26 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; | ||
27 | import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; | ||
28 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
29 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
30 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
31 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
32 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
33 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
34 | import tools.refinery.viatra.runtime.rete.network.communication.NodeComparator; | ||
35 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
36 | import tools.refinery.viatra.runtime.rete.single.DiscriminatorDispatcherNode; | ||
37 | |||
38 | /** | ||
39 | * Timely (DDF) implementation of the {@link CommunicationTracker}. | ||
40 | * | ||
41 | * @author Tamas Szabo | ||
42 | * @since 2.3 | ||
43 | */ | ||
44 | public class TimelyCommunicationTracker extends CommunicationTracker { | ||
45 | |||
46 | protected final TimelyConfiguration configuration; | ||
47 | |||
48 | public TimelyCommunicationTracker(final TimelyConfiguration configuration) { | ||
49 | this.configuration = configuration; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | protected CommunicationGroup createGroup(final Node representative, final int index) { | ||
54 | final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1; | ||
55 | return new TimelyCommunicationGroup(this, representative, index, isSingleton); | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | protected void reconstructQueueContents(final Set<CommunicationGroup> oldActiveGroups) { | ||
60 | for (final CommunicationGroup oldGroup : oldActiveGroups) { | ||
61 | for (final Entry<MessageSelector, Collection<Mailbox>> entry : oldGroup.getMailboxes().entrySet()) { | ||
62 | for (final Mailbox mailbox : entry.getValue()) { | ||
63 | final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver()); | ||
64 | newGroup.notifyHasMessage(mailbox, entry.getKey()); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public Mailbox proxifyMailbox(final Node requester, final Mailbox original) { | ||
72 | final Mailbox mailboxToProxify = (original instanceof TimelyMailboxProxy) | ||
73 | ? ((TimelyMailboxProxy) original).getWrappedMailbox() | ||
74 | : original; | ||
75 | final TimestampTransformation preprocessor = getPreprocessor(requester, mailboxToProxify.getReceiver()); | ||
76 | if (preprocessor == null) { | ||
77 | return mailboxToProxify; | ||
78 | } else { | ||
79 | return new TimelyMailboxProxy(mailboxToProxify, preprocessor); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) { | ||
85 | final IndexerListener listenerToProxify = (original instanceof TimelyIndexerListenerProxy) | ||
86 | ? ((TimelyIndexerListenerProxy) original).getWrappedIndexerListener() | ||
87 | : original; | ||
88 | final TimestampTransformation preprocessor = getPreprocessor(requester, listenerToProxify.getOwner()); | ||
89 | if (preprocessor == null) { | ||
90 | return listenerToProxify; | ||
91 | } else { | ||
92 | return new TimelyIndexerListenerProxy(listenerToProxify, preprocessor); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | protected TimestampTransformation getPreprocessor(final Node source, final Node target) { | ||
97 | final Node effectiveSource = source instanceof SpecializedProjectionIndexer | ||
98 | ? ((SpecializedProjectionIndexer) source).getActiveNode() | ||
99 | : source; | ||
100 | final CommunicationGroup sourceGroup = this.getGroup(effectiveSource); | ||
101 | final CommunicationGroup targetGroup = this.getGroup(target); | ||
102 | |||
103 | if (sourceGroup != null && targetGroup != null) { | ||
104 | // during RETE construction, the groups may be still null | ||
105 | if (sourceGroup != targetGroup && sourceGroup.isRecursive()) { | ||
106 | // targetGroup is a successor SCC of sourceGroup | ||
107 | // and sourceGroup is a recursive SCC | ||
108 | // then we need to zero out the timestamps | ||
109 | return TimestampTransformation.RESET; | ||
110 | } | ||
111 | if (sourceGroup == targetGroup && target instanceof ProductionNode) { | ||
112 | // if requester and receiver are in the same SCC | ||
113 | // and receiver is a production node | ||
114 | // then we need to increment the timestamps | ||
115 | return TimestampTransformation.INCREMENT; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | return null; | ||
120 | } | ||
121 | |||
122 | @Override | ||
123 | protected void postProcessNode(final Node node) { | ||
124 | if (node instanceof NetworkStructureChangeSensitiveNode) { | ||
125 | ((NetworkStructureChangeSensitiveNode) node).networkStructureChanged(); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | protected void postProcessGroup(final CommunicationGroup group) { | ||
131 | if (this.configuration.getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) { | ||
132 | final Node representative = group.getRepresentative(); | ||
133 | final Set<Node> groupMembers = this.sccInformationProvider.sccs.getPartition(representative); | ||
134 | if (groupMembers.size() > 1) { | ||
135 | final Graph<Node> graph = new Graph<Node>(); | ||
136 | |||
137 | for (final Node node : groupMembers) { | ||
138 | graph.insertNode(node); | ||
139 | } | ||
140 | |||
141 | for (final Node source : groupMembers) { | ||
142 | for (final Node target : this.dependencyGraph.getTargetNodes(source)) { | ||
143 | // (1) the edge is not a recursion cut point | ||
144 | // (2) the edge is within this group | ||
145 | if (!this.isRecursionCutPoint(source, target) && groupMembers.contains(target)) { | ||
146 | graph.insertEdge(source, target); | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | final List<Node> orderedNodes = TopologicalSorting.compute(graph); | ||
152 | final Map<Node, Integer> nodeMap = CollectionsFactory.createMap(); | ||
153 | int identifier = 0; | ||
154 | for (final Node orderedNode : orderedNodes) { | ||
155 | nodeMap.put(orderedNode, identifier++); | ||
156 | } | ||
157 | |||
158 | ((TimelyCommunicationGroup) group).setComparatorAndReorderMailboxes(new NodeComparator(nodeMap)); | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * This static field is used for debug purposes in the DotGenerator. | ||
165 | */ | ||
166 | public static final Function<Node, Function<Node, String>> EDGE_LABEL_FUNCTION = new Function<Node, Function<Node, String>>() { | ||
167 | |||
168 | @Override | ||
169 | public Function<Node, String> apply(final Node source) { | ||
170 | return new Function<Node, String>() { | ||
171 | @Override | ||
172 | public String apply(final Node target) { | ||
173 | if (source instanceof SpecializedProjectionIndexer) { | ||
174 | final Collection<ListenerSubscription> subscriptions = ((SpecializedProjectionIndexer) source) | ||
175 | .getSubscriptions(); | ||
176 | for (final ListenerSubscription subscription : subscriptions) { | ||
177 | if (subscription.getListener().getOwner() == target | ||
178 | && subscription.getListener() instanceof TimelyIndexerListenerProxy) { | ||
179 | return ((TimelyIndexerListenerProxy) subscription.getListener()).preprocessor | ||
180 | .toString(); | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | if (source instanceof StandardIndexer) { | ||
185 | final Collection<IndexerListener> listeners = ((StandardIndexer) source).getListeners(); | ||
186 | for (final IndexerListener listener : listeners) { | ||
187 | if (listener.getOwner() == target && listener instanceof TimelyIndexerListenerProxy) { | ||
188 | return ((TimelyIndexerListenerProxy) listener).preprocessor.toString(); | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | if (source instanceof StandardNode) { | ||
193 | final Collection<Mailbox> mailboxes = ((StandardNode) source).getChildMailboxes(); | ||
194 | for (final Mailbox mailbox : mailboxes) { | ||
195 | if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) { | ||
196 | return ((TimelyMailboxProxy) mailbox).preprocessor.toString(); | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | if (source instanceof DiscriminatorDispatcherNode) { | ||
201 | final Collection<Mailbox> mailboxes = ((DiscriminatorDispatcherNode) source) | ||
202 | .getBucketMailboxes().values(); | ||
203 | for (final Mailbox mailbox : mailboxes) { | ||
204 | if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) { | ||
205 | return ((TimelyMailboxProxy) mailbox).preprocessor.toString(); | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | return null; | ||
210 | } | ||
211 | }; | ||
212 | } | ||
213 | |||
214 | }; | ||
215 | |||
216 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java new file mode 100644 index 00000000..e8fbf84e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java | |||
@@ -0,0 +1,81 @@ | |||
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.rete.network.communication.timely; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
14 | import tools.refinery.viatra.runtime.rete.index.IndexerListener; | ||
15 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
16 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
17 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
18 | |||
19 | /** | ||
20 | * A timely proxy for another {@link IndexerListener}, which performs some preprocessing | ||
21 | * on the differential timestamps before passing it on to the real recipient. | ||
22 | * <p> | ||
23 | * These proxies are used on edges leading into {@link ProductionNode}s. Because {@link ProductionNode}s | ||
24 | * never ask back the indexer for its contents, there is no need to also apply the proxy on that direction. | ||
25 | * | ||
26 | * @author Tamas Szabo | ||
27 | * @since 2.3 | ||
28 | */ | ||
29 | public class TimelyIndexerListenerProxy implements IndexerListener { | ||
30 | |||
31 | protected final TimestampTransformation preprocessor; | ||
32 | protected final IndexerListener wrapped; | ||
33 | |||
34 | public TimelyIndexerListenerProxy(final IndexerListener wrapped, | ||
35 | final TimestampTransformation preprocessor) { | ||
36 | Preconditions.checkArgument(!(wrapped instanceof TimelyIndexerListenerProxy), "Proxy in a proxy is not allowed!"); | ||
37 | this.wrapped = wrapped; | ||
38 | this.preprocessor = preprocessor; | ||
39 | } | ||
40 | |||
41 | public IndexerListener getWrappedIndexerListener() { | ||
42 | return wrapped; | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public Node getOwner() { | ||
47 | return this.wrapped.getOwner(); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, final Tuple signature, | ||
52 | final boolean change, final Timestamp timestamp) { | ||
53 | this.wrapped.notifyIndexerUpdate(direction, updateElement, signature, change, preprocessor.process(timestamp)); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public String toString() { | ||
58 | return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public boolean equals(final Object obj) { | ||
63 | if (obj == null || obj.getClass() != this.getClass()) { | ||
64 | return false; | ||
65 | } else if (obj == this) { | ||
66 | return true; | ||
67 | } else { | ||
68 | final TimelyIndexerListenerProxy that = (TimelyIndexerListenerProxy) obj; | ||
69 | return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public int hashCode() { | ||
75 | int hash = 1; | ||
76 | hash = hash * 17 + this.wrapped.hashCode(); | ||
77 | hash = hash * 31 + this.preprocessor.hashCode(); | ||
78 | return hash; | ||
79 | } | ||
80 | |||
81 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java new file mode 100644 index 00000000..550bfbeb --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java | |||
@@ -0,0 +1,102 @@ | |||
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.rete.network.communication.timely; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
14 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
15 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
16 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
17 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
18 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
19 | |||
20 | /** | ||
21 | * A timely proxy for another {@link Mailbox}, which performs some preprocessing | ||
22 | * on the differential timestamps before passing it on to the real recipient. | ||
23 | * | ||
24 | * @author Tamas Szabo | ||
25 | * @since 2.3 | ||
26 | */ | ||
27 | public class TimelyMailboxProxy implements Mailbox { | ||
28 | |||
29 | protected final TimestampTransformation preprocessor; | ||
30 | protected final Mailbox wrapped; | ||
31 | |||
32 | public TimelyMailboxProxy(final Mailbox wrapped, final TimestampTransformation preprocessor) { | ||
33 | Preconditions.checkArgument(!(wrapped instanceof TimelyMailboxProxy), "Proxy in a proxy is not allowed!"); | ||
34 | this.wrapped = wrapped; | ||
35 | this.preprocessor = preprocessor; | ||
36 | } | ||
37 | |||
38 | public Mailbox getWrappedMailbox() { | ||
39 | return wrapped; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
44 | this.wrapped.postMessage(direction, update, preprocessor.process(timestamp)); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public String toString() { | ||
49 | return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString(); | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void clear() { | ||
54 | this.wrapped.clear(); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public void deliverAll(final MessageSelector selector) { | ||
59 | this.wrapped.deliverAll(selector); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public CommunicationGroup getCurrentGroup() { | ||
64 | return this.wrapped.getCurrentGroup(); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public void setCurrentGroup(final CommunicationGroup group) { | ||
69 | this.wrapped.setCurrentGroup(group); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public Receiver getReceiver() { | ||
74 | return this.wrapped.getReceiver(); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public boolean isEmpty() { | ||
79 | return this.wrapped.isEmpty(); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public boolean equals(final Object obj) { | ||
84 | if (obj == null || obj.getClass() != this.getClass()) { | ||
85 | return false; | ||
86 | } else if (obj == this) { | ||
87 | return true; | ||
88 | } else { | ||
89 | final TimelyMailboxProxy that = (TimelyMailboxProxy) obj; | ||
90 | return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public int hashCode() { | ||
96 | int hash = 1; | ||
97 | hash = hash * 17 + this.wrapped.hashCode(); | ||
98 | hash = hash * 31 + this.preprocessor.hashCode(); | ||
99 | return hash; | ||
100 | } | ||
101 | |||
102 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java new file mode 100644 index 00000000..8929eb5c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java | |||
@@ -0,0 +1,48 @@ | |||
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.rete.network.communication.timely; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
12 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
13 | |||
14 | /** | ||
15 | * Values of this enum perform different kind of preprocessing on {@link Timestamp}s. | ||
16 | * This is used on edges leading in and out from {@link Node}s in recursive {@link TimelyCommunicationGroup}s. | ||
17 | * | ||
18 | * @author Tamas Szabo | ||
19 | * @since 2.3 | ||
20 | */ | ||
21 | public enum TimestampTransformation { | ||
22 | |||
23 | INCREMENT { | ||
24 | @Override | ||
25 | public Timestamp process(final Timestamp timestamp) { | ||
26 | return new Timestamp(timestamp.getValue() + 1); | ||
27 | } | ||
28 | |||
29 | @Override | ||
30 | public String toString() { | ||
31 | return "INCREMENT"; | ||
32 | } | ||
33 | }, | ||
34 | RESET { | ||
35 | @Override | ||
36 | public Timestamp process(final Timestamp timestamp) { | ||
37 | return Timestamp.ZERO; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public String toString() { | ||
42 | return "RESET"; | ||
43 | } | ||
44 | }; | ||
45 | |||
46 | public abstract Timestamp process(final Timestamp timestamp); | ||
47 | |||
48 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java new file mode 100644 index 00000000..d6312671 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java | |||
@@ -0,0 +1,81 @@ | |||
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.rete.network.delayed; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
19 | import tools.refinery.viatra.runtime.rete.network.Network; | ||
20 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
21 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
22 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
23 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
26 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
27 | |||
28 | /** | ||
29 | * Instances of this class are responsible for initializing a {@link Receiver} with the contents of a {@link Supplier}. | ||
30 | * However, due to the dynamic nature of the Rete {@link Network} and to the fact that certain {@link Node}s in the | ||
31 | * {@link Network} are sensitive to the shape of the {@link Network}, the commands must be delayed until the | ||
32 | * construction of the {@link Network} has stabilized. | ||
33 | * | ||
34 | * @author Tamas Szabo | ||
35 | * @since 2.3 | ||
36 | */ | ||
37 | public abstract class DelayedCommand implements Runnable { | ||
38 | |||
39 | protected final Supplier supplier; | ||
40 | protected final Receiver receiver; | ||
41 | protected final Direction direction; | ||
42 | protected final ReteContainer container; | ||
43 | |||
44 | public DelayedCommand(final Supplier supplier, final Receiver receiver, final Direction direction, | ||
45 | final ReteContainer container) { | ||
46 | this.supplier = supplier; | ||
47 | this.receiver = receiver; | ||
48 | this.direction = direction; | ||
49 | this.container = container; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void run() { | ||
54 | final CommunicationTracker tracker = this.container.getCommunicationTracker(); | ||
55 | final Mailbox mailbox = tracker.proxifyMailbox(this.supplier, this.receiver.getMailbox()); | ||
56 | |||
57 | if (this.isTimestampAware()) { | ||
58 | final Map<Tuple, Timeline<Timestamp>> contents = this.container.pullContentsWithTimeline(this.supplier, | ||
59 | false); | ||
60 | for (final Entry<Tuple, Timeline<Timestamp>> entry : contents.entrySet()) { | ||
61 | for (final Signed<Timestamp> change : entry.getValue().asChangeSequence()) { | ||
62 | mailbox.postMessage(change.getDirection().multiply(this.direction), entry.getKey(), | ||
63 | change.getPayload()); | ||
64 | } | ||
65 | } | ||
66 | } else { | ||
67 | final Collection<Tuple> contents = this.container.pullContents(this.supplier, false); | ||
68 | for (final Tuple tuple : contents) { | ||
69 | mailbox.postMessage(this.direction, tuple, Timestamp.ZERO); | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public String toString() { | ||
76 | return this.supplier + " -> " + this.receiver.toString(); | ||
77 | } | ||
78 | |||
79 | protected abstract boolean isTimestampAware(); | ||
80 | |||
81 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java new file mode 100644 index 00000000..1bfdbec6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java | |||
@@ -0,0 +1,27 @@ | |||
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.rete.network.delayed; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
12 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
13 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
14 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
15 | |||
16 | public class DelayedConnectCommand extends DelayedCommand { | ||
17 | |||
18 | public DelayedConnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container) { | ||
19 | super(supplier, receiver, Direction.INSERT, container); | ||
20 | } | ||
21 | |||
22 | @Override | ||
23 | protected boolean isTimestampAware() { | ||
24 | return this.container.isTimelyEvaluation() && this.container.getCommunicationTracker().areInSameGroup(this.supplier, this.receiver); | ||
25 | } | ||
26 | |||
27 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java new file mode 100644 index 00000000..5825a971 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java | |||
@@ -0,0 +1,30 @@ | |||
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.rete.network.delayed; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
12 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
13 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
14 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
15 | |||
16 | public class DelayedDisconnectCommand extends DelayedCommand { | ||
17 | |||
18 | protected final boolean wasInSameSCC; | ||
19 | |||
20 | public DelayedDisconnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container, final boolean wasInSameSCC) { | ||
21 | super(supplier, receiver, Direction.DELETE, container); | ||
22 | this.wasInSameSCC = wasInSameSCC; | ||
23 | } | ||
24 | |||
25 | @Override | ||
26 | protected boolean isTimestampAware() { | ||
27 | return this.wasInSameSCC; | ||
28 | } | ||
29 | |||
30 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java new file mode 100644 index 00000000..da9bc47e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java | |||
@@ -0,0 +1,74 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.indexer; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
16 | |||
17 | /** | ||
18 | * @author Tamas Szabo | ||
19 | * @since 2.0 | ||
20 | */ | ||
21 | public class DefaultMessageIndexer implements MessageIndexer { | ||
22 | |||
23 | protected final Map<Tuple, Integer> indexer; | ||
24 | |||
25 | public DefaultMessageIndexer() { | ||
26 | this.indexer = CollectionsFactory.createMap(); | ||
27 | } | ||
28 | |||
29 | public Map<Tuple, Integer> getTuples() { | ||
30 | return Collections.unmodifiableMap(this.indexer); | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public int getCount(final Tuple update) { | ||
35 | final Integer count = getTuples().get(update); | ||
36 | if (count == null) { | ||
37 | return 0; | ||
38 | } else { | ||
39 | return count; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void insert(final Tuple update) { | ||
45 | update(update, 1); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public void delete(final Tuple update) { | ||
50 | update(update, -1); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public void update(final Tuple update, final int delta) { | ||
55 | final Integer oldCount = this.indexer.get(update); | ||
56 | final int newCount = (oldCount == null ? 0 : oldCount) + delta; | ||
57 | if (newCount == 0) { | ||
58 | this.indexer.remove(update); | ||
59 | } else { | ||
60 | this.indexer.put(update, newCount); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public boolean isEmpty() { | ||
66 | return this.indexer.isEmpty(); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public void clear() { | ||
71 | this.indexer.clear(); | ||
72 | } | ||
73 | |||
74 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java new file mode 100644 index 00000000..80271252 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java | |||
@@ -0,0 +1,95 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.indexer; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
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 | |||
19 | /** | ||
20 | * @author Tamas Szabo | ||
21 | * @since 2.0 | ||
22 | */ | ||
23 | public class GroupBasedMessageIndexer implements MessageIndexer { | ||
24 | |||
25 | protected final Map<Tuple, DefaultMessageIndexer> indexer; | ||
26 | protected final TupleMask groupMask; | ||
27 | |||
28 | public GroupBasedMessageIndexer(final TupleMask groupMask) { | ||
29 | this.indexer = CollectionsFactory.createMap(); | ||
30 | this.groupMask = groupMask; | ||
31 | } | ||
32 | |||
33 | public Map<Tuple, Integer> getTuplesByGroup(final Tuple group) { | ||
34 | final DefaultMessageIndexer values = this.indexer.get(group); | ||
35 | if (values == null) { | ||
36 | return Collections.emptyMap(); | ||
37 | } else { | ||
38 | return Collections.unmodifiableMap(values.getTuples()); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public int getCount(final Tuple update) { | ||
44 | final Tuple group = this.groupMask.transform(update); | ||
45 | final Integer count = getTuplesByGroup(group).get(update); | ||
46 | if (count == null) { | ||
47 | return 0; | ||
48 | } else { | ||
49 | return count; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | public Set<Tuple> getGroups() { | ||
54 | return Collections.unmodifiableSet(this.indexer.keySet()); | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public void insert(final Tuple update) { | ||
59 | update(update, 1); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public void delete(final Tuple update) { | ||
64 | update(update, -1); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public void update(final Tuple update, final int delta) { | ||
69 | final Tuple group = this.groupMask.transform(update); | ||
70 | DefaultMessageIndexer valueIndexer = this.indexer.get(group); | ||
71 | |||
72 | if (valueIndexer == null) { | ||
73 | valueIndexer = new DefaultMessageIndexer(); | ||
74 | this.indexer.put(group, valueIndexer); | ||
75 | } | ||
76 | |||
77 | valueIndexer.update(update, delta); | ||
78 | |||
79 | // it may happen that the indexer becomes empty as a result of the update | ||
80 | if (valueIndexer.isEmpty()) { | ||
81 | this.indexer.remove(group); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public boolean isEmpty() { | ||
87 | return this.indexer.isEmpty(); | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public void clear() { | ||
92 | this.indexer.clear(); | ||
93 | } | ||
94 | |||
95 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java new file mode 100644 index 00000000..271aaa44 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.indexer; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
13 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
14 | |||
15 | /** | ||
16 | * A message indexer is used by {@link Mailbox}es to index their contents. | ||
17 | * | ||
18 | * @author Tamas Szabo | ||
19 | * @since 2.0 | ||
20 | */ | ||
21 | public interface MessageIndexer extends Clearable { | ||
22 | |||
23 | public void insert(final Tuple update); | ||
24 | |||
25 | public void delete(final Tuple update); | ||
26 | |||
27 | public void update(final Tuple update, final int delta); | ||
28 | |||
29 | public boolean isEmpty(); | ||
30 | |||
31 | public int getCount(final Tuple update); | ||
32 | |||
33 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java new file mode 100644 index 00000000..99097f56 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.mailbox; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
12 | import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy; | ||
13 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
14 | |||
15 | /** | ||
16 | * An adaptable mailbox can be wrapped by another mailbox to act in behalf of that. The significance of the adaptation | ||
17 | * is that the adaptee will notify the {@link CommunicationTracker} about updates by promoting the adapter itself. | ||
18 | * Adaptable mailboxes are used by the {@link BehaviorChangingMailbox}. | ||
19 | * | ||
20 | * Compare this with {@link TimelyMailboxProxy}. That one also wraps another mailbox in order to | ||
21 | * perform preprocessing on the messages sent to the original recipient. | ||
22 | * | ||
23 | * @author Tamas Szabo | ||
24 | * @since 2.0 | ||
25 | */ | ||
26 | public interface AdaptableMailbox extends Mailbox { | ||
27 | |||
28 | public Mailbox getAdapter(); | ||
29 | |||
30 | public void setAdapter(final Mailbox adapter); | ||
31 | |||
32 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java new file mode 100644 index 00000000..8797e254 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java | |||
@@ -0,0 +1,30 @@ | |||
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.rete.network.mailbox; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
12 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
13 | |||
14 | /** | ||
15 | * A fall through capable mailbox can directly call the update method of its {@link Receiver} instead of using the | ||
16 | * standard post-deliver mailbox semantics. If the fall through flag is set to true, the mailbox uses direct delivery, | ||
17 | * otherwise it operates in the original behavior. The fall through operation is preferable whenever applicable because | ||
18 | * it improves performance. The fall through flag is controlled by the {@link CommunicationTracker} based on the | ||
19 | * receiver node type and network topology. | ||
20 | * | ||
21 | * @author Tamas Szabo | ||
22 | * @since 2.2 | ||
23 | */ | ||
24 | public interface FallThroughCapableMailbox extends Mailbox { | ||
25 | |||
26 | public boolean isFallThrough(); | ||
27 | |||
28 | public void setFallThrough(final boolean fallThrough); | ||
29 | |||
30 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java new file mode 100644 index 00000000..05005974 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java | |||
@@ -0,0 +1,78 @@ | |||
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.rete.network.mailbox; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
14 | import tools.refinery.viatra.runtime.rete.network.IGroupable; | ||
15 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
16 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
17 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
18 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
19 | |||
20 | /** | ||
21 | * A mailbox is associated with every {@link Receiver}. Messages can be sent to a {@link Receiver} by posting them into | ||
22 | * the mailbox. Different mailbox implementations may differ in the way how they deliver the posted messages. | ||
23 | * | ||
24 | * @author Tamas Szabo | ||
25 | * @since 2.0 | ||
26 | * | ||
27 | */ | ||
28 | public interface Mailbox extends Clearable, IGroupable { | ||
29 | |||
30 | /** | ||
31 | * Posts a new message to this mailbox. | ||
32 | * | ||
33 | * @param direction | ||
34 | * the direction of the update | ||
35 | * @param update | ||
36 | * the update element | ||
37 | * @since 2.4 | ||
38 | */ | ||
39 | public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp); | ||
40 | |||
41 | /** | ||
42 | * Delivers all messages according to the given selector from this mailbox. The selector can also be null. In this case, no | ||
43 | * special separation is expected between the messages. | ||
44 | * | ||
45 | * @param selector the message selector | ||
46 | */ | ||
47 | public void deliverAll(final MessageSelector selector); | ||
48 | |||
49 | /** | ||
50 | * Returns the {@link Receiver} of this mailbox. | ||
51 | * | ||
52 | * @return the receiver | ||
53 | */ | ||
54 | public Receiver getReceiver(); | ||
55 | |||
56 | /** | ||
57 | * Returns the {@link CommunicationGroup} of the receiver of this mailbox. | ||
58 | * | ||
59 | * @return the communication group | ||
60 | */ | ||
61 | public CommunicationGroup getCurrentGroup(); | ||
62 | |||
63 | /** | ||
64 | * Sets the {@link CommunicationGroup} that the receiver of this mailbox is associated with. | ||
65 | * | ||
66 | * @param group | ||
67 | * the communication group | ||
68 | */ | ||
69 | public void setCurrentGroup(final CommunicationGroup group); | ||
70 | |||
71 | /** | ||
72 | * Returns true if this mailbox is empty. | ||
73 | * | ||
74 | * @return | ||
75 | */ | ||
76 | public boolean isEmpty(); | ||
77 | |||
78 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java new file mode 100644 index 00000000..2c5255fb --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.mailbox; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer; | ||
12 | |||
13 | /** | ||
14 | * A factory used to create message indexers for {@link Mailbox}es. | ||
15 | * | ||
16 | * @author Tamas Szabo | ||
17 | * @since 2.0 | ||
18 | */ | ||
19 | public interface MessageIndexerFactory<I extends MessageIndexer> { | ||
20 | |||
21 | public I create(); | ||
22 | |||
23 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java new file mode 100644 index 00000000..1e1ada71 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java | |||
@@ -0,0 +1,109 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, 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.rete.network.mailbox.timeless; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
12 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
13 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
14 | import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer; | ||
15 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
16 | import tools.refinery.viatra.runtime.rete.network.mailbox.MessageIndexerFactory; | ||
17 | |||
18 | /** | ||
19 | * An abstract mailbox implementation that is capable of splitting update messages based on some form of monotonicity | ||
20 | * (anti-monotone and monotone). The monotonicity is either defined by the less or equal operator of a poset or, it can | ||
21 | * be the standard subset ordering among sets of tuples. | ||
22 | * | ||
23 | * @author Tamas Szabo | ||
24 | * @since 2.0 | ||
25 | * | ||
26 | */ | ||
27 | public abstract class AbstractUpdateSplittingMailbox<IndexerType extends MessageIndexer, ReceiverType extends Receiver> implements Mailbox { | ||
28 | |||
29 | protected IndexerType monotoneQueue; | ||
30 | protected IndexerType antiMonotoneQueue; | ||
31 | protected IndexerType monotoneBuffer; | ||
32 | protected IndexerType antiMonotoneBuffer; | ||
33 | protected boolean deliveringMonotone; | ||
34 | protected boolean deliveringAntiMonotone; | ||
35 | protected final ReceiverType receiver; | ||
36 | protected final ReteContainer container; | ||
37 | protected CommunicationGroup group; | ||
38 | |||
39 | public AbstractUpdateSplittingMailbox(final ReceiverType receiver, final ReteContainer container, | ||
40 | final MessageIndexerFactory<IndexerType> factory) { | ||
41 | this.receiver = receiver; | ||
42 | this.container = container; | ||
43 | this.monotoneQueue = factory.create(); | ||
44 | this.antiMonotoneQueue = factory.create(); | ||
45 | this.monotoneBuffer = factory.create(); | ||
46 | this.antiMonotoneBuffer = factory.create(); | ||
47 | this.deliveringMonotone = false; | ||
48 | this.deliveringAntiMonotone = false; | ||
49 | } | ||
50 | |||
51 | protected void swapAndClearMonotone() { | ||
52 | final IndexerType tmp = this.monotoneQueue; | ||
53 | this.monotoneQueue = this.monotoneBuffer; | ||
54 | this.monotoneBuffer = tmp; | ||
55 | this.monotoneBuffer.clear(); | ||
56 | } | ||
57 | |||
58 | protected void swapAndClearAntiMonotone() { | ||
59 | final IndexerType tmp = this.antiMonotoneQueue; | ||
60 | this.antiMonotoneQueue = this.antiMonotoneBuffer; | ||
61 | this.antiMonotoneBuffer = tmp; | ||
62 | this.antiMonotoneBuffer.clear(); | ||
63 | } | ||
64 | |||
65 | protected IndexerType getActiveMonotoneQueue() { | ||
66 | if (this.deliveringMonotone) { | ||
67 | return this.monotoneBuffer; | ||
68 | } else { | ||
69 | return this.monotoneQueue; | ||
70 | } | ||
71 | } | ||
72 | |||
73 | protected IndexerType getActiveAntiMonotoneQueue() { | ||
74 | if (this.deliveringAntiMonotone) { | ||
75 | return this.antiMonotoneBuffer; | ||
76 | } else { | ||
77 | return this.antiMonotoneQueue; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public ReceiverType getReceiver() { | ||
83 | return this.receiver; | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void clear() { | ||
88 | this.monotoneQueue.clear(); | ||
89 | this.antiMonotoneQueue.clear(); | ||
90 | this.monotoneBuffer.clear(); | ||
91 | this.antiMonotoneBuffer.clear(); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public boolean isEmpty() { | ||
96 | return this.getActiveMonotoneQueue().isEmpty() && this.getActiveAntiMonotoneQueue().isEmpty(); | ||
97 | } | ||
98 | |||
99 | @Override | ||
100 | public CommunicationGroup getCurrentGroup() { | ||
101 | return this.group; | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public void setCurrentGroup(final CommunicationGroup group) { | ||
106 | this.group = group; | ||
107 | } | ||
108 | |||
109 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java new file mode 100644 index 00000000..fe822d7c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java | |||
@@ -0,0 +1,117 @@ | |||
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.rete.network.mailbox.timeless; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
12 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
13 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
14 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
15 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
16 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
17 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
18 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker; | ||
21 | import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox; | ||
22 | import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox; | ||
23 | |||
24 | /** | ||
25 | * This mailbox changes its behavior based on the position of its {@link Receiver} in the network topology. | ||
26 | * It either behaves as a {@link DefaultMailbox} or as an {@link UpdateSplittingMailbox}. The decision is made by the | ||
27 | * {@link CommunicationTracker}, see {@link TimelessCommunicationTracker#postProcessNode(Node)} for more details. | ||
28 | * | ||
29 | * @author Tamas Szabo | ||
30 | */ | ||
31 | public class BehaviorChangingMailbox implements FallThroughCapableMailbox { | ||
32 | |||
33 | protected boolean fallThrough; | ||
34 | protected boolean split; | ||
35 | protected AdaptableMailbox wrapped; | ||
36 | protected final Receiver receiver; | ||
37 | protected final ReteContainer container; | ||
38 | protected CommunicationGroup group; | ||
39 | |||
40 | public BehaviorChangingMailbox(final Receiver receiver, final ReteContainer container) { | ||
41 | this.fallThrough = false; | ||
42 | this.split = false; | ||
43 | this.receiver = receiver; | ||
44 | this.container = container; | ||
45 | this.wrapped = new DefaultMailbox(receiver, container); | ||
46 | this.wrapped.setAdapter(this); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
51 | if (this.fallThrough && !this.container.isExecutingDelayedCommands()) { | ||
52 | // disable fall through while we are in the middle of executing delayed construction commands | ||
53 | this.receiver.update(direction, update, timestamp); | ||
54 | } else { | ||
55 | this.wrapped.postMessage(direction, update, timestamp); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | @Override | ||
60 | public void deliverAll(final MessageSelector kind) { | ||
61 | this.wrapped.deliverAll(kind); | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public String toString() { | ||
66 | return "A_MBOX -> " + this.wrapped; | ||
67 | } | ||
68 | |||
69 | public void setSplitFlag(final boolean splitValue) { | ||
70 | if (this.split != splitValue) { | ||
71 | assert isEmpty(); | ||
72 | if (splitValue) { | ||
73 | this.wrapped = new UpdateSplittingMailbox(this.receiver, this.container); | ||
74 | } else { | ||
75 | this.wrapped = new DefaultMailbox(this.receiver, this.container); | ||
76 | } | ||
77 | this.wrapped.setAdapter(this); | ||
78 | this.split = splitValue; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public boolean isEmpty() { | ||
84 | return this.wrapped.isEmpty(); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public void clear() { | ||
89 | this.wrapped.clear(); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public Receiver getReceiver() { | ||
94 | return this.receiver; | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public CommunicationGroup getCurrentGroup() { | ||
99 | return this.group; | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public void setCurrentGroup(final CommunicationGroup group) { | ||
104 | this.group = group; | ||
105 | } | ||
106 | |||
107 | @Override | ||
108 | public boolean isFallThrough() { | ||
109 | return this.fallThrough; | ||
110 | } | ||
111 | |||
112 | @Override | ||
113 | public void setFallThrough(final boolean fallThrough) { | ||
114 | this.fallThrough = fallThrough; | ||
115 | } | ||
116 | |||
117 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java new file mode 100644 index 00000000..baf7270f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java | |||
@@ -0,0 +1,163 @@ | |||
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.rete.network.mailbox.timeless; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
16 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
17 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
18 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; | ||
21 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
22 | import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox; | ||
23 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
24 | |||
25 | /** | ||
26 | * Default mailbox implementation. | ||
27 | * <p> | ||
28 | * Usually, the mailbox performs counting of messages so that they can cancel each other out. However, if marked as a | ||
29 | * fall-through mailbox, than update messages are delivered directly to the receiver node to reduce overhead. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * @since 2.0 | ||
33 | */ | ||
34 | public class DefaultMailbox implements AdaptableMailbox { | ||
35 | |||
36 | private static int SIZE_TRESHOLD = 127; | ||
37 | |||
38 | protected Map<Tuple, Integer> queue; | ||
39 | protected Map<Tuple, Integer> buffer; | ||
40 | protected final Receiver receiver; | ||
41 | protected final ReteContainer container; | ||
42 | protected boolean delivering; | ||
43 | protected Mailbox adapter; | ||
44 | protected CommunicationGroup group; | ||
45 | |||
46 | public DefaultMailbox(final Receiver receiver, final ReteContainer container) { | ||
47 | this.receiver = receiver; | ||
48 | this.container = container; | ||
49 | this.queue = CollectionsFactory.createMap(); | ||
50 | this.buffer = CollectionsFactory.createMap(); | ||
51 | this.adapter = this; | ||
52 | } | ||
53 | |||
54 | protected Map<Tuple, Integer> getActiveQueue() { | ||
55 | if (this.delivering) { | ||
56 | return this.buffer; | ||
57 | } else { | ||
58 | return this.queue; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public Mailbox getAdapter() { | ||
64 | return this.adapter; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public void setAdapter(final Mailbox adapter) { | ||
69 | this.adapter = adapter; | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public boolean isEmpty() { | ||
74 | return getActiveQueue().isEmpty(); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
79 | final Map<Tuple, Integer> activeQueue = getActiveQueue(); | ||
80 | final boolean wasEmpty = activeQueue.isEmpty(); | ||
81 | |||
82 | boolean significantChange = false; | ||
83 | Integer count = activeQueue.get(update); | ||
84 | if (count == null) { | ||
85 | count = 0; | ||
86 | significantChange = true; | ||
87 | } | ||
88 | |||
89 | if (direction == Direction.DELETE) { | ||
90 | count--; | ||
91 | } else { | ||
92 | count++; | ||
93 | } | ||
94 | |||
95 | if (count == 0) { | ||
96 | activeQueue.remove(update); | ||
97 | significantChange = true; | ||
98 | } else { | ||
99 | activeQueue.put(update, count); | ||
100 | } | ||
101 | |||
102 | if (significantChange) { | ||
103 | final Mailbox targetMailbox = this.adapter; | ||
104 | final CommunicationGroup targetGroup = this.adapter.getCurrentGroup(); | ||
105 | |||
106 | if (wasEmpty) { | ||
107 | targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.DEFAULT); | ||
108 | } else if (activeQueue.isEmpty()) { | ||
109 | targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.DEFAULT); | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public void deliverAll(final MessageSelector kind) { | ||
116 | if (kind == PhasedSelector.DEFAULT) { | ||
117 | // use the buffer during delivering so that there is a clear | ||
118 | // separation between the stages | ||
119 | this.delivering = true; | ||
120 | this.receiver.batchUpdate(this.queue.entrySet(), Timestamp.ZERO); | ||
121 | this.delivering = false; | ||
122 | |||
123 | if (queue.size() > SIZE_TRESHOLD) { | ||
124 | this.queue = this.buffer; | ||
125 | this.buffer = CollectionsFactory.createMap(); | ||
126 | } else { | ||
127 | this.queue.clear(); | ||
128 | final Map<Tuple, Integer> tmpQueue = this.queue; | ||
129 | this.queue = this.buffer; | ||
130 | this.buffer = tmpQueue; | ||
131 | } | ||
132 | } else { | ||
133 | throw new IllegalArgumentException("Unsupported message kind " + kind); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | @Override | ||
138 | public String toString() { | ||
139 | return "D_MBOX (" + this.receiver + ") " + this.getActiveQueue(); | ||
140 | } | ||
141 | |||
142 | @Override | ||
143 | public Receiver getReceiver() { | ||
144 | return this.receiver; | ||
145 | } | ||
146 | |||
147 | @Override | ||
148 | public void clear() { | ||
149 | this.queue.clear(); | ||
150 | this.buffer.clear(); | ||
151 | } | ||
152 | |||
153 | @Override | ||
154 | public CommunicationGroup getCurrentGroup() { | ||
155 | return this.group; | ||
156 | } | ||
157 | |||
158 | @Override | ||
159 | public void setCurrentGroup(final CommunicationGroup group) { | ||
160 | this.group = group; | ||
161 | } | ||
162 | |||
163 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java new file mode 100644 index 00000000..50d19882 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java | |||
@@ -0,0 +1,218 @@ | |||
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.rete.network.mailbox.timeless; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Map.Entry; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
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.CollectionsFactory; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
20 | import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
25 | import tools.refinery.viatra.runtime.rete.network.indexer.GroupBasedMessageIndexer; | ||
26 | |||
27 | /** | ||
28 | * A monotonicity aware mailbox implementation. The mailbox uses an {@link IPosetComparator} to identify if a pair of | ||
29 | * REVOKE - INSERT updates represent a monotone change pair. The mailbox is used by {@link PosetAwareReceiver}s. | ||
30 | * | ||
31 | * @author Tamas Szabo | ||
32 | * @since 2.0 | ||
33 | */ | ||
34 | public class PosetAwareMailbox extends AbstractUpdateSplittingMailbox<GroupBasedMessageIndexer, PosetAwareReceiver> { | ||
35 | |||
36 | protected final TupleMask groupMask; | ||
37 | |||
38 | public PosetAwareMailbox(final PosetAwareReceiver receiver, final ReteContainer container) { | ||
39 | super(receiver, container, () -> new GroupBasedMessageIndexer(receiver.getCoreMask())); | ||
40 | this.groupMask = receiver.getCoreMask(); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
45 | final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue(); | ||
46 | final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue(); | ||
47 | final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0; | ||
48 | final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0; | ||
49 | final TupleMask coreMask = this.receiver.getCoreMask(); | ||
50 | |||
51 | // it cannot happen that it was present in both | ||
52 | assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone); | ||
53 | |||
54 | if (direction == Direction.INSERT) { | ||
55 | if (wasPresentAsAntiMonotone) { | ||
56 | // it was an anti-monotone one before | ||
57 | antiMonotoneQueue.insert(update); | ||
58 | } else { | ||
59 | // it was a monotone one before or did not exist at all | ||
60 | monotoneQueue.insert(update); | ||
61 | |||
62 | // if it was not present in the monotone queue before, then | ||
63 | // we need to check whether it makes REVOKE updates monotone | ||
64 | if (!wasPresentAsMonotone) { | ||
65 | final Set<Tuple> counterParts = tryFindCounterPart(update, false, true); | ||
66 | for (final Tuple counterPart : counterParts) { | ||
67 | final int count = antiMonotoneQueue.getCount(counterPart); | ||
68 | assert count < 0; | ||
69 | antiMonotoneQueue.update(counterPart, -count); | ||
70 | monotoneQueue.update(counterPart, count); | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | } else { | ||
75 | if (wasPresentAsAntiMonotone) { | ||
76 | // it was an anti-monotone one before | ||
77 | antiMonotoneQueue.delete(update); | ||
78 | } else if (wasPresentAsMonotone) { | ||
79 | // it was a monotone one before | ||
80 | monotoneQueue.delete(update); | ||
81 | |||
82 | // and we need to check whether the monotone REVOKE updates | ||
83 | // still have a reinforcing counterpart | ||
84 | final Set<Tuple> candidates = new HashSet<Tuple>(); | ||
85 | final Tuple key = coreMask.transform(update); | ||
86 | for (final Entry<Tuple, Integer> entry : monotoneQueue.getTuplesByGroup(key).entrySet()) { | ||
87 | if (entry.getValue() < 0) { | ||
88 | final Tuple candidate = entry.getKey(); | ||
89 | final Set<Tuple> counterParts = tryFindCounterPart(candidate, true, false); | ||
90 | if (counterParts.isEmpty()) { | ||
91 | // all of them are gone | ||
92 | candidates.add(candidate); | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | // move the candidates from the monotone queue to the | ||
98 | // anti-monotone queue because they do not have a | ||
99 | // counterpart anymore | ||
100 | for (final Tuple candidate : candidates) { | ||
101 | final int count = monotoneQueue.getCount(candidate); | ||
102 | assert count < 0; | ||
103 | monotoneQueue.update(candidate, -count); | ||
104 | antiMonotoneQueue.update(candidate, count); | ||
105 | } | ||
106 | } else { | ||
107 | // it did not exist before | ||
108 | final Set<Tuple> counterParts = tryFindCounterPart(update, true, false); | ||
109 | if (counterParts.isEmpty()) { | ||
110 | // there is no tuple that would make this update monotone | ||
111 | antiMonotoneQueue.delete(update); | ||
112 | } else { | ||
113 | // there is a reinforcing counterpart | ||
114 | monotoneQueue.delete(update); | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | |||
119 | if (antiMonotoneQueue.isEmpty()) { | ||
120 | this.group.notifyLostAllMessages(this, PhasedSelector.ANTI_MONOTONE); | ||
121 | } else { | ||
122 | this.group.notifyHasMessage(this, PhasedSelector.ANTI_MONOTONE); | ||
123 | } | ||
124 | |||
125 | if (monotoneQueue.isEmpty()) { | ||
126 | this.group.notifyLostAllMessages(this, PhasedSelector.MONOTONE); | ||
127 | } else { | ||
128 | this.group.notifyHasMessage(this, PhasedSelector.MONOTONE); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | protected Set<Tuple> tryFindCounterPart(final Tuple first, final boolean findPositiveCounterPart, | ||
133 | final boolean findAllCounterParts) { | ||
134 | final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue(); | ||
135 | final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue(); | ||
136 | final TupleMask coreMask = this.receiver.getCoreMask(); | ||
137 | final TupleMask posetMask = this.receiver.getPosetMask(); | ||
138 | final IPosetComparator posetComparator = this.receiver.getPosetComparator(); | ||
139 | final Set<Tuple> result = CollectionsFactory.createSet(); | ||
140 | final Tuple firstKey = coreMask.transform(first); | ||
141 | final Tuple firstValue = posetMask.transform(first); | ||
142 | |||
143 | if (findPositiveCounterPart) { | ||
144 | for (final Entry<Tuple, Integer> entry : monotoneQueue.getTuplesByGroup(firstKey).entrySet()) { | ||
145 | final Tuple secondValue = posetMask.transform(entry.getKey()); | ||
146 | if (entry.getValue() > 0 && posetComparator.isLessOrEqual(firstValue, secondValue)) { | ||
147 | result.add(entry.getKey()); | ||
148 | if (!findAllCounterParts) { | ||
149 | return result; | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | } else { | ||
154 | for (final Entry<Tuple, Integer> entry : antiMonotoneQueue.getTuplesByGroup(firstKey).entrySet()) { | ||
155 | final Tuple secondValue = posetMask.transform(entry.getKey()); | ||
156 | if (posetComparator.isLessOrEqual(secondValue, firstValue)) { | ||
157 | result.add(entry.getKey()); | ||
158 | if (!findAllCounterParts) { | ||
159 | return result; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | return result; | ||
166 | } | ||
167 | |||
168 | @Override | ||
169 | public void deliverAll(final MessageSelector kind) { | ||
170 | if (kind == PhasedSelector.ANTI_MONOTONE) { | ||
171 | // use the buffer during delivering so that there is a clear | ||
172 | // separation between the stages | ||
173 | this.deliveringAntiMonotone = true; | ||
174 | |||
175 | for (final Tuple group : this.antiMonotoneQueue.getGroups()) { | ||
176 | for (final Entry<Tuple, Integer> entry : this.antiMonotoneQueue.getTuplesByGroup(group).entrySet()) { | ||
177 | final Tuple update = entry.getKey(); | ||
178 | final int count = entry.getValue(); | ||
179 | assert count < 0; | ||
180 | for (int i = 0; i < Math.abs(count); i++) { | ||
181 | this.receiver.updateWithPosetInfo(Direction.DELETE, update, false); | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | |||
186 | this.deliveringAntiMonotone = false; | ||
187 | swapAndClearAntiMonotone(); | ||
188 | } else if (kind == PhasedSelector.MONOTONE) { | ||
189 | // use the buffer during delivering so that there is a clear | ||
190 | // separation between the stages | ||
191 | this.deliveringMonotone = true; | ||
192 | |||
193 | for (final Tuple group : this.monotoneQueue.getGroups()) { | ||
194 | for (final Entry<Tuple, Integer> entry : this.monotoneQueue.getTuplesByGroup(group).entrySet()) { | ||
195 | final Tuple update = entry.getKey(); | ||
196 | final int count = entry.getValue(); | ||
197 | assert count != 0; | ||
198 | final Direction direction = count < 0 ? Direction.DELETE : Direction.INSERT; | ||
199 | for (int i = 0; i < Math.abs(count); i++) { | ||
200 | this.receiver.updateWithPosetInfo(direction, update, true); | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | |||
205 | this.deliveringMonotone = false; | ||
206 | swapAndClearMonotone(); | ||
207 | } else { | ||
208 | throw new IllegalArgumentException("Unsupported message kind " + kind); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | @Override | ||
213 | public String toString() { | ||
214 | return "PA_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " " | ||
215 | + this.getActiveAntiMonotoneQueue(); | ||
216 | } | ||
217 | |||
218 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java new file mode 100644 index 00000000..afa155b2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java | |||
@@ -0,0 +1,135 @@ | |||
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.rete.network.mailbox.timeless; | ||
10 | |||
11 | import java.util.Map.Entry; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
14 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
15 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
16 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
17 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
18 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | import tools.refinery.viatra.runtime.rete.network.indexer.DefaultMessageIndexer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox; | ||
23 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
24 | |||
25 | /** | ||
26 | * A mailbox implementation that splits updates messages according to the standard subset ordering into anti-monotonic | ||
27 | * (deletions) and monotonic (insertions) updates. | ||
28 | * | ||
29 | * @author Tamas Szabo | ||
30 | * @since 2.0 | ||
31 | */ | ||
32 | public class UpdateSplittingMailbox extends AbstractUpdateSplittingMailbox<DefaultMessageIndexer, Receiver> | ||
33 | implements AdaptableMailbox { | ||
34 | |||
35 | protected Mailbox adapter; | ||
36 | |||
37 | public UpdateSplittingMailbox(final Receiver receiver, final ReteContainer container) { | ||
38 | super(receiver, container, DefaultMessageIndexer::new); | ||
39 | this.adapter = this; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public Mailbox getAdapter() { | ||
44 | return this.adapter; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public void setAdapter(final Mailbox adapter) { | ||
49 | this.adapter = adapter; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
54 | final DefaultMessageIndexer monotoneQueue = getActiveMonotoneQueue(); | ||
55 | final DefaultMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue(); | ||
56 | final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0; | ||
57 | final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0; | ||
58 | |||
59 | // it cannot happen that it was present in both | ||
60 | assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone); | ||
61 | |||
62 | if (direction == Direction.INSERT) { | ||
63 | if (wasPresentAsAntiMonotone) { | ||
64 | // it was an anti-monotone one before | ||
65 | antiMonotoneQueue.insert(update); | ||
66 | } else { | ||
67 | // it was a monotone one before or did not exist at all | ||
68 | monotoneQueue.insert(update); | ||
69 | } | ||
70 | } else { | ||
71 | if (wasPresentAsMonotone) { | ||
72 | // it was a monotone one before | ||
73 | monotoneQueue.delete(update); | ||
74 | } else { | ||
75 | // it was an anti-monotone one before or did not exist at all | ||
76 | antiMonotoneQueue.delete(update); | ||
77 | } | ||
78 | } | ||
79 | |||
80 | final Mailbox targetMailbox = this.adapter; | ||
81 | final CommunicationGroup targetGroup = this.adapter.getCurrentGroup(); | ||
82 | |||
83 | if (antiMonotoneQueue.isEmpty()) { | ||
84 | targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.ANTI_MONOTONE); | ||
85 | } else { | ||
86 | targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.ANTI_MONOTONE); | ||
87 | } | ||
88 | |||
89 | if (monotoneQueue.isEmpty()) { | ||
90 | targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.MONOTONE); | ||
91 | } else { | ||
92 | targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.MONOTONE); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public void deliverAll(final MessageSelector kind) { | ||
98 | if (kind == PhasedSelector.ANTI_MONOTONE) { | ||
99 | // deliver anti-monotone | ||
100 | this.deliveringAntiMonotone = true; | ||
101 | for (final Entry<Tuple, Integer> entry : this.antiMonotoneQueue.getTuples().entrySet()) { | ||
102 | final Tuple update = entry.getKey(); | ||
103 | final int count = entry.getValue(); | ||
104 | assert count < 0; | ||
105 | for (int i = 0; i < Math.abs(count); i++) { | ||
106 | this.receiver.update(Direction.DELETE, update, Timestamp.ZERO); | ||
107 | } | ||
108 | } | ||
109 | this.deliveringAntiMonotone = false; | ||
110 | swapAndClearAntiMonotone(); | ||
111 | } else if (kind == PhasedSelector.MONOTONE) { | ||
112 | // deliver monotone | ||
113 | this.deliveringMonotone = true; | ||
114 | for (final Entry<Tuple, Integer> entry : this.monotoneQueue.getTuples().entrySet()) { | ||
115 | final Tuple update = entry.getKey(); | ||
116 | final int count = entry.getValue(); | ||
117 | assert count > 0; | ||
118 | for (int i = 0; i < count; i++) { | ||
119 | this.receiver.update(Direction.INSERT, update, Timestamp.ZERO); | ||
120 | } | ||
121 | } | ||
122 | this.deliveringMonotone = false; | ||
123 | swapAndClearMonotone(); | ||
124 | } else { | ||
125 | throw new IllegalArgumentException("Unsupported message kind " + kind); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public String toString() { | ||
131 | return "US_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " " | ||
132 | + this.getActiveAntiMonotoneQueue(); | ||
133 | } | ||
134 | |||
135 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java new file mode 100644 index 00000000..bf3b8e14 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java | |||
@@ -0,0 +1,150 @@ | |||
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.rete.network.mailbox.timely; | ||
10 | |||
11 | import java.util.Map; | ||
12 | import java.util.TreeMap; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; | ||
18 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
19 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
21 | import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; | ||
24 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
25 | |||
26 | public class TimelyMailbox implements Mailbox { | ||
27 | |||
28 | protected TreeMap<Timestamp, Map<Tuple, Integer>> queue; | ||
29 | protected final Receiver receiver; | ||
30 | protected final ReteContainer container; | ||
31 | protected CommunicationGroup group; | ||
32 | protected boolean fallThrough; | ||
33 | |||
34 | public TimelyMailbox(final Receiver receiver, final ReteContainer container) { | ||
35 | this.receiver = receiver; | ||
36 | this.container = container; | ||
37 | this.queue = CollectionsFactory.createTreeMap(); | ||
38 | } | ||
39 | |||
40 | protected TreeMap<Timestamp, Map<Tuple, Integer>> getActiveQueue() { | ||
41 | return this.queue; | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public boolean isEmpty() { | ||
46 | return getActiveQueue().isEmpty(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
51 | final TreeMap<Timestamp, Map<Tuple, Integer>> activeQueue = getActiveQueue(); | ||
52 | |||
53 | Map<Tuple, Integer> tupleMap = activeQueue.get(timestamp); | ||
54 | final boolean wasEmpty = tupleMap == null; | ||
55 | boolean significantChange = false; | ||
56 | |||
57 | if (tupleMap == null) { | ||
58 | tupleMap = CollectionsFactory.createMap(); | ||
59 | activeQueue.put(timestamp, tupleMap); | ||
60 | significantChange = true; | ||
61 | } | ||
62 | |||
63 | Integer count = tupleMap.get(update); | ||
64 | if (count == null) { | ||
65 | count = 0; | ||
66 | significantChange = true; | ||
67 | } | ||
68 | |||
69 | if (direction == Direction.DELETE) { | ||
70 | count--; | ||
71 | } else { | ||
72 | count++; | ||
73 | } | ||
74 | |||
75 | if (count == 0) { | ||
76 | tupleMap.remove(update); | ||
77 | if (tupleMap.isEmpty()) { | ||
78 | activeQueue.remove(timestamp); | ||
79 | } | ||
80 | significantChange = true; | ||
81 | } else { | ||
82 | tupleMap.put(update, count); | ||
83 | } | ||
84 | |||
85 | if (significantChange) { | ||
86 | if (wasEmpty) { | ||
87 | this.group.notifyHasMessage(this, timestamp); | ||
88 | } else if (tupleMap.isEmpty()) { | ||
89 | final Timestamp resumableTimestamp = (this.receiver instanceof ResumableNode) | ||
90 | ? ((ResumableNode) this.receiver).getResumableTimestamp() | ||
91 | : null; | ||
92 | // check if there is folding left to do before unsubscribing just based on the message queue being empty | ||
93 | if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) { | ||
94 | this.group.notifyLostAllMessages(this, timestamp); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public void deliverAll(final MessageSelector selector) { | ||
102 | if (selector instanceof Timestamp) { | ||
103 | final Timestamp timestamp = (Timestamp) selector; | ||
104 | // REMOVE the tuples associated with the selector, dont just query them | ||
105 | final Map<Tuple, Integer> tupleMap = this.queue.remove(timestamp); | ||
106 | |||
107 | // tupleMap may be empty if we only have lazy folding to do | ||
108 | if (tupleMap != null) { | ||
109 | this.receiver.batchUpdate(tupleMap.entrySet(), timestamp); | ||
110 | } | ||
111 | |||
112 | if (this.container.getTimelyConfiguration() | ||
113 | .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) { | ||
114 | // (1) either normal delivery, which ended up being a lazy folding state | ||
115 | // (2) and/or lazy folding needs to be resumed | ||
116 | if (this.receiver instanceof ResumableNode) { | ||
117 | ((ResumableNode) this.receiver).resumeAt(timestamp); | ||
118 | } | ||
119 | } | ||
120 | } else { | ||
121 | throw new IllegalArgumentException("Unsupported message selector " + selector); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | @Override | ||
126 | public String toString() { | ||
127 | return "DDF_MBOX (" + this.receiver + ") " + this.getActiveQueue(); | ||
128 | } | ||
129 | |||
130 | @Override | ||
131 | public Receiver getReceiver() { | ||
132 | return this.receiver; | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public void clear() { | ||
137 | this.queue.clear(); | ||
138 | } | ||
139 | |||
140 | @Override | ||
141 | public CommunicationGroup getCurrentGroup() { | ||
142 | return this.group; | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public void setCurrentGroup(final CommunicationGroup group) { | ||
147 | this.group = group; | ||
148 | } | ||
149 | |||
150 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java new file mode 100644 index 00000000..2fed3225 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java | |||
@@ -0,0 +1,125 @@ | |||
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.rete.remote; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
13 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
14 | |||
15 | /** | ||
16 | * Remote identifier of a node of type T. | ||
17 | * | ||
18 | * @author Gabor Bergmann | ||
19 | * | ||
20 | */ | ||
21 | public class Address<T extends Node> { | ||
22 | ReteContainer container; | ||
23 | Long nodeId; | ||
24 | /** | ||
25 | * Feel free to leave null e.g. if node is in a separate JVM. | ||
26 | */ | ||
27 | T nodeCache; | ||
28 | |||
29 | /** | ||
30 | * Address of local node (use only for containers in the same VM!) | ||
31 | */ | ||
32 | public static <N extends Node> Address<N> of(N node) { | ||
33 | return new Address<N>(node); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * General constructor. | ||
38 | * | ||
39 | * @param container | ||
40 | * @param nodeId | ||
41 | */ | ||
42 | public Address(ReteContainer container, Long nodeId) { | ||
43 | super(); | ||
44 | this.container = container; | ||
45 | this.nodeId = nodeId; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Local-only constructor. (use only for containers in the same VM!) | ||
50 | * | ||
51 | * @param node | ||
52 | * the node to address | ||
53 | */ | ||
54 | public Address(T node) { | ||
55 | super(); | ||
56 | this.nodeCache = node; | ||
57 | this.container = node.getContainer(); | ||
58 | this.nodeId = node.getNodeId(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public int hashCode() { | ||
63 | final int prime = 31; | ||
64 | int result = 1; | ||
65 | result = prime * result + ((container == null) ? 0 : container.hashCode()); | ||
66 | result = prime * result + ((nodeId == null) ? 0 : nodeId.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 Address<?>)) | ||
77 | return false; | ||
78 | final Address<?> other = (Address<?>) obj; | ||
79 | if (container == null) { | ||
80 | if (other.container != null) | ||
81 | return false; | ||
82 | } else if (!container.equals(other.container)) | ||
83 | return false; | ||
84 | if (nodeId == null) { | ||
85 | if (other.nodeId != null) | ||
86 | return false; | ||
87 | } else if (!nodeId.equals(other.nodeId)) | ||
88 | return false; | ||
89 | return true; | ||
90 | } | ||
91 | |||
92 | public ReteContainer getContainer() { | ||
93 | return container; | ||
94 | } | ||
95 | |||
96 | public void setContainer(ReteContainer container) { | ||
97 | this.container = container; | ||
98 | } | ||
99 | |||
100 | public Long getNodeId() { | ||
101 | return nodeId; | ||
102 | } | ||
103 | |||
104 | public void setNodeId(Long nodeId) { | ||
105 | this.nodeId = nodeId; | ||
106 | } | ||
107 | |||
108 | public T getNodeCache() { | ||
109 | return nodeCache; | ||
110 | } | ||
111 | |||
112 | public void setNodeCache(T nodeCache) { | ||
113 | this.nodeCache = nodeCache; | ||
114 | } | ||
115 | |||
116 | @Override | ||
117 | public String toString() { | ||
118 | if (nodeCache == null) | ||
119 | return "A(" + nodeId + " @ " + container + ")"; | ||
120 | else | ||
121 | return "A(" + nodeCache + ")"; | ||
122 | |||
123 | } | ||
124 | |||
125 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java new file mode 100644 index 00000000..f7d267af --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java | |||
@@ -0,0 +1,63 @@ | |||
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.rete.remote; | ||
11 | |||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collection; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
20 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
23 | import tools.refinery.viatra.runtime.rete.single.SingleInputNode; | ||
24 | |||
25 | /** | ||
26 | * This node delivers updates to a remote recipient; no updates are propagated further in this network. | ||
27 | * | ||
28 | * @author Gabor Bergmann | ||
29 | * | ||
30 | */ | ||
31 | public class RemoteReceiver extends SingleInputNode { | ||
32 | |||
33 | List<Address<? extends Receiver>> targets; | ||
34 | |||
35 | public RemoteReceiver(ReteContainer reteContainer) { | ||
36 | super(reteContainer); | ||
37 | targets = new ArrayList<Address<? extends Receiver>>(); | ||
38 | } | ||
39 | |||
40 | public void addTarget(Address<? extends Receiver> target) { | ||
41 | targets.add(target); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public void pullInto(Collection<Tuple> collector, boolean flush) { | ||
46 | propagatePullInto(collector, flush); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) { | ||
51 | throw new UnsupportedOperationException(); | ||
52 | } | ||
53 | |||
54 | public Collection<Tuple> remotePull(boolean flush) { | ||
55 | return reteContainer.pullContents(this, flush); | ||
56 | } | ||
57 | |||
58 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
59 | for (Address<? extends Receiver> ad : targets) | ||
60 | reteContainer.sendUpdateToRemoteAddress(ad, direction, updateElement); | ||
61 | } | ||
62 | |||
63 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java new file mode 100644 index 00000000..cbe4d177 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java | |||
@@ -0,0 +1,54 @@ | |||
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.rete.remote; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | import tools.refinery.viatra.runtime.rete.single.SingleInputNode; | ||
21 | |||
22 | /** | ||
23 | * This node receives updates from a remote supplier; no local updates are expected. | ||
24 | * | ||
25 | * @author Gabor Bergmann | ||
26 | * | ||
27 | */ | ||
28 | public class RemoteSupplier extends SingleInputNode { | ||
29 | |||
30 | RemoteReceiver counterpart; | ||
31 | |||
32 | public RemoteSupplier(ReteContainer reteContainer, RemoteReceiver counterpart) { | ||
33 | super(reteContainer); | ||
34 | this.counterpart = counterpart; | ||
35 | counterpart.addTarget(reteContainer.makeAddress(this)); | ||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public void pullInto(Collection<Tuple> collector, boolean flush) { | ||
40 | Collection<Tuple> pulled = counterpart.remotePull(flush); | ||
41 | collector.addAll(pulled); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) { | ||
46 | throw new UnsupportedOperationException(); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
51 | propagateUpdate(direction, updateElement, timestamp); | ||
52 | } | ||
53 | |||
54 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java new file mode 100644 index 00000000..e92ce63f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java | |||
@@ -0,0 +1,138 @@ | |||
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.rete.single; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.List; | ||
14 | import java.util.Set; | ||
15 | |||
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.Direction; | ||
19 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
20 | import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
22 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
23 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
24 | import tools.refinery.viatra.runtime.rete.network.Tunnel; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
26 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
27 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
28 | import tools.refinery.viatra.runtime.rete.util.Options; | ||
29 | |||
30 | /** | ||
31 | * Ensures that no identical copies get to the output. Only one replica of each pattern substitution may traverse this | ||
32 | * node. There are both timeless and timely implementations. | ||
33 | * | ||
34 | * @author Gabor Bergmann | ||
35 | * @author Tamas Szabo | ||
36 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
37 | * @noextend This class is not intended to be subclassed by clients. | ||
38 | * @since 2.2 | ||
39 | */ | ||
40 | public abstract class AbstractUniquenessEnforcerNode extends StandardNode implements Tunnel { | ||
41 | |||
42 | protected final Collection<Supplier> parents; | ||
43 | protected ProjectionIndexer memoryNullIndexer; | ||
44 | protected ProjectionIndexer memoryIdentityIndexer; | ||
45 | protected final int tupleWidth; | ||
46 | // MUST BE INSTANTIATED IN THE CONCRETE SUBCLASSES AFTER ALL FIELDS ARE SET | ||
47 | protected Mailbox mailbox; | ||
48 | protected final TupleMask nullMask; | ||
49 | protected final TupleMask identityMask; | ||
50 | protected final List<ListenerSubscription> specializedListeners; | ||
51 | |||
52 | public AbstractUniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) { | ||
53 | super(reteContainer); | ||
54 | this.parents = new ArrayList<Supplier>(); | ||
55 | this.specializedListeners = new ArrayList<ListenerSubscription>(); | ||
56 | this.tupleWidth = tupleWidth; | ||
57 | this.nullMask = TupleMask.linear(0, tupleWidth); | ||
58 | this.identityMask = TupleMask.identity(tupleWidth); | ||
59 | } | ||
60 | |||
61 | protected abstract Mailbox instantiateMailbox(); | ||
62 | |||
63 | @Override | ||
64 | public Mailbox getMailbox() { | ||
65 | return this.mailbox; | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * @since 2.8 | ||
70 | */ | ||
71 | public abstract Set<Tuple> getTuples(); | ||
72 | |||
73 | /** | ||
74 | * @since 2.4 | ||
75 | */ | ||
76 | protected void propagate(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
77 | // See Bug 518434 | ||
78 | // trivial (non-active) indexers must be updated before other listeners | ||
79 | // so that if they are joined against each other, trivial indexers lookups | ||
80 | // will be consistent with their notifications; | ||
81 | // also, their subscriptions must share a single order | ||
82 | for (final ListenerSubscription subscription : specializedListeners) { | ||
83 | subscription.propagate(direction, update, timestamp); | ||
84 | } | ||
85 | propagateUpdate(direction, update, timestamp); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) { | ||
90 | if (Options.employTrivialIndexers) { | ||
91 | if (nullMask.equals(mask)) { | ||
92 | final ProjectionIndexer indexer = getNullIndexer(); | ||
93 | for (final TraceInfo traceInfo : traces) { | ||
94 | indexer.assignTraceInfo(traceInfo); | ||
95 | } | ||
96 | return indexer; | ||
97 | } | ||
98 | if (identityMask.equals(mask)) { | ||
99 | final ProjectionIndexer indexer = getIdentityIndexer(); | ||
100 | for (final TraceInfo traceInfo : traces) { | ||
101 | indexer.assignTraceInfo(traceInfo); | ||
102 | } | ||
103 | return indexer; | ||
104 | } | ||
105 | } | ||
106 | return super.constructIndex(mask, traces); | ||
107 | } | ||
108 | |||
109 | public abstract ProjectionIndexer getNullIndexer(); | ||
110 | |||
111 | public abstract ProjectionIndexer getIdentityIndexer(); | ||
112 | |||
113 | @Override | ||
114 | public void appendParent(final Supplier supplier) { | ||
115 | parents.add(supplier); | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public void removeParent(final Supplier supplier) { | ||
120 | parents.remove(supplier); | ||
121 | } | ||
122 | |||
123 | @Override | ||
124 | public Collection<Supplier> getParents() { | ||
125 | return parents; | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public void assignTraceInfo(final TraceInfo traceInfo) { | ||
130 | super.assignTraceInfo(traceInfo); | ||
131 | if (traceInfo.propagateFromStandardNodeToSupplierParent()) { | ||
132 | for (final Supplier parent : parents) { | ||
133 | parent.acceptPropagatedTraceInfo(traceInfo); | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java new file mode 100644 index 00000000..c68036b5 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, 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.rete.single; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.IUpdateable; | ||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
14 | import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver; | ||
15 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
16 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
17 | |||
18 | /** | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public class CallbackNode extends SimpleReceiver { | ||
23 | |||
24 | IUpdateable updateable; | ||
25 | |||
26 | public CallbackNode(ReteContainer reteContainer, IUpdateable updateable) | ||
27 | { | ||
28 | super(reteContainer); | ||
29 | this.updateable = updateable; | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
34 | updateable.update(updateElement, direction == Direction.INSERT); | ||
35 | } | ||
36 | |||
37 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java new file mode 100644 index 00000000..eca8bc17 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java | |||
@@ -0,0 +1,79 @@ | |||
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.rete.single; | ||
11 | |||
12 | import java.util.Iterator; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
19 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
20 | import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; | ||
21 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
22 | |||
23 | /** | ||
24 | * Default implementation of the Production node, based on UniquenessEnforcerNode | ||
25 | * | ||
26 | * @author Gabor Bergmann | ||
27 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
28 | */ | ||
29 | public class DefaultProductionNode extends UniquenessEnforcerNode implements ProductionNode { | ||
30 | |||
31 | protected final Map<String, Integer> posMapping; | ||
32 | |||
33 | /** | ||
34 | * @since 1.6 | ||
35 | */ | ||
36 | public DefaultProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping, | ||
37 | final boolean deleteRederiveEvaluation) { | ||
38 | this(reteContainer, posMapping, deleteRederiveEvaluation, null, null, null); | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * @since 1.6 | ||
43 | */ | ||
44 | public DefaultProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping, | ||
45 | final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask, | ||
46 | final IPosetComparator posetComparator) { | ||
47 | super(reteContainer, posMapping.size(), deleteRederiveEvaluation, coreMask, posetMask, posetComparator); | ||
48 | this.posMapping = posMapping; | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public Map<String, Integer> getPosMapping() { | ||
53 | return posMapping; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public Iterator<Tuple> iterator() { | ||
58 | return memory.iterator(); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) { | ||
63 | if (traceInfo.propagateToProductionNodeParentAlso()) { | ||
64 | super.acceptPropagatedTraceInfo(traceInfo); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public String toString() { | ||
70 | for (final TraceInfo traceInfo : this.traceInfos) { | ||
71 | if (traceInfo instanceof CompiledQuery) { | ||
72 | final String patternName = ((CompiledQuery) traceInfo).getPatternName(); | ||
73 | return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString()); | ||
74 | } | ||
75 | } | ||
76 | return super.toString(); | ||
77 | } | ||
78 | |||
79 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java new file mode 100644 index 00000000..803bab20 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java | |||
@@ -0,0 +1,85 @@ | |||
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.rete.single; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | |||
22 | /** | ||
23 | * A bucket holds a filtered set of tuples of its parent {@link DiscriminatorDispatcherNode}. | ||
24 | * Exactly those that have the given bucket key at their discrimination column. | ||
25 | * | ||
26 | * <p> During operation, tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)} | ||
27 | * | ||
28 | * @author Gabor Bergmann | ||
29 | * @since 1.5 | ||
30 | */ | ||
31 | public class DiscriminatorBucketNode extends SingleInputNode { | ||
32 | |||
33 | private Object bucketKey; | ||
34 | |||
35 | /** | ||
36 | * @param bucketKey will be wrapped using {@link IQueryRuntimeContext#wrapElement(Object)} | ||
37 | |||
38 | */ | ||
39 | public DiscriminatorBucketNode(ReteContainer reteContainer, Object bucketKey) { | ||
40 | super(reteContainer); | ||
41 | this.bucketKey = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapElement(bucketKey); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
46 | if (parent != null) { | ||
47 | getDispatcher().pullIntoFiltered(collector, bucketKey, flush); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
53 | if (parent != null) { | ||
54 | getDispatcher().pullIntoWithTimestampFiltered(collector, bucketKey, flush); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
60 | propagateUpdate(direction, updateElement, timestamp); | ||
61 | } | ||
62 | |||
63 | public Object getBucketKey() { | ||
64 | return bucketKey; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public void appendParent(Supplier supplier) { | ||
69 | if (! (supplier instanceof DiscriminatorDispatcherNode)) | ||
70 | throw new IllegalArgumentException(); | ||
71 | super.appendParent(supplier); | ||
72 | } | ||
73 | |||
74 | public DiscriminatorDispatcherNode getDispatcher() { | ||
75 | return (DiscriminatorDispatcherNode) parent; | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | protected String toStringCore() { | ||
80 | return String.format("%s<%s=='%s'>", | ||
81 | super.toStringCore(), | ||
82 | (getDispatcher() == null) ? "?" : getDispatcher().getDiscriminationColumnIndex(), | ||
83 | bucketKey); | ||
84 | } | ||
85 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java new file mode 100644 index 00000000..a8e11fcd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java | |||
@@ -0,0 +1,154 @@ | |||
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.rete.single; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.Map; | ||
15 | import java.util.Map.Entry; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; | ||
18 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
22 | import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; | ||
23 | import tools.refinery.viatra.runtime.rete.network.Receiver; | ||
24 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
25 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
26 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
27 | |||
28 | /** | ||
29 | * Node that sends tuples off to different buckets (attached as children of type {@link DiscriminatorBucketNode}), based | ||
30 | * on the value of a given column. | ||
31 | * | ||
32 | * <p> | ||
33 | * Tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)} | ||
34 | * | ||
35 | * @author Gabor Bergmann | ||
36 | * @since 1.5 | ||
37 | */ | ||
38 | public class DiscriminatorDispatcherNode extends SingleInputNode implements NetworkStructureChangeSensitiveNode { | ||
39 | |||
40 | private int discriminationColumnIndex; | ||
41 | private Map<Object, DiscriminatorBucketNode> buckets = new HashMap<>(); | ||
42 | private Map<Object, Mailbox> bucketMailboxes = new HashMap<>(); | ||
43 | |||
44 | /** | ||
45 | * @param reteContainer | ||
46 | */ | ||
47 | public DiscriminatorDispatcherNode(ReteContainer reteContainer, int discriminationColumnIndex) { | ||
48 | super(reteContainer); | ||
49 | this.discriminationColumnIndex = discriminationColumnIndex; | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
54 | Object dispatchKey = updateElement.get(discriminationColumnIndex); | ||
55 | Mailbox bucketMailBox = bucketMailboxes.get(dispatchKey); | ||
56 | if (bucketMailBox != null) { | ||
57 | bucketMailBox.postMessage(direction, updateElement, timestamp); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | public int getDiscriminationColumnIndex() { | ||
62 | return discriminationColumnIndex; | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
67 | propagatePullInto(collector, flush); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
72 | propagatePullIntoWithTimestamp(collector, flush); | ||
73 | } | ||
74 | |||
75 | /** | ||
76 | * @since 2.3 | ||
77 | */ | ||
78 | public void pullIntoFiltered(final Collection<Tuple> collector, final Object bucketKey, final boolean flush) { | ||
79 | final ArrayList<Tuple> unfiltered = new ArrayList<Tuple>(); | ||
80 | propagatePullInto(unfiltered, flush); | ||
81 | for (Tuple tuple : unfiltered) { | ||
82 | if (bucketKey.equals(tuple.get(discriminationColumnIndex))) { | ||
83 | collector.add(tuple); | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * @since 2.3 | ||
90 | */ | ||
91 | public void pullIntoWithTimestampFiltered(final Map<Tuple, Timeline<Timestamp>> collector, final Object bucketKey, | ||
92 | final boolean flush) { | ||
93 | final Map<Tuple, Timeline<Timestamp>> unfiltered = CollectionsFactory.createMap(); | ||
94 | propagatePullIntoWithTimestamp(unfiltered, flush); | ||
95 | for (final Entry<Tuple, Timeline<Timestamp>> entry : unfiltered.entrySet()) { | ||
96 | if (bucketKey.equals(entry.getKey().get(discriminationColumnIndex))) { | ||
97 | collector.put(entry.getKey(), entry.getValue()); | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public void appendChild(Receiver receiver) { | ||
104 | super.appendChild(receiver); | ||
105 | if (receiver instanceof DiscriminatorBucketNode) { | ||
106 | DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver; | ||
107 | Object bucketKey = bucket.getBucketKey(); | ||
108 | DiscriminatorBucketNode old = buckets.put(bucketKey, bucket); | ||
109 | if (old != null) { | ||
110 | throw new IllegalStateException(); | ||
111 | } | ||
112 | bucketMailboxes.put(bucketKey, this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox())); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * @since 2.2 | ||
118 | */ | ||
119 | public Map<Object, Mailbox> getBucketMailboxes() { | ||
120 | return this.bucketMailboxes; | ||
121 | } | ||
122 | |||
123 | @Override | ||
124 | public void networkStructureChanged() { | ||
125 | bucketMailboxes.clear(); | ||
126 | for (Receiver receiver : children) { | ||
127 | if (receiver instanceof DiscriminatorBucketNode) { | ||
128 | DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver; | ||
129 | Object bucketKey = bucket.getBucketKey(); | ||
130 | bucketMailboxes.put(bucketKey, | ||
131 | this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox())); | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public void removeChild(Receiver receiver) { | ||
138 | super.removeChild(receiver); | ||
139 | if (receiver instanceof DiscriminatorBucketNode) { | ||
140 | DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver; | ||
141 | Object bucketKey = bucket.getBucketKey(); | ||
142 | DiscriminatorBucketNode old = buckets.remove(bucketKey); | ||
143 | if (old != bucket) | ||
144 | throw new IllegalStateException(); | ||
145 | bucketMailboxes.remove(bucketKey); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | @Override | ||
150 | protected String toStringCore() { | ||
151 | return super.toStringCore() + '<' + discriminationColumnIndex + '>'; | ||
152 | } | ||
153 | |||
154 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java new file mode 100644 index 00000000..014c2016 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java | |||
@@ -0,0 +1,41 @@ | |||
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.rete.single; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
14 | |||
15 | public class EqualityFilterNode extends FilterNode { | ||
16 | |||
17 | int[] indices; | ||
18 | int first; | ||
19 | |||
20 | /** | ||
21 | * @param reteContainer | ||
22 | * @param indices | ||
23 | * indices of the Tuple that should hold equal values | ||
24 | */ | ||
25 | public EqualityFilterNode(ReteContainer reteContainer, int[] indices) { | ||
26 | super(reteContainer); | ||
27 | this.indices = indices; | ||
28 | first = indices[0]; | ||
29 | } | ||
30 | |||
31 | @Override | ||
32 | public boolean check(Tuple ps) { | ||
33 | Object firstElement = ps.get(first); | ||
34 | for (int i = 1 /* first is omitted */; i < indices.length; i++) { | ||
35 | if (!ps.get(indices[i]).equals(firstElement)) | ||
36 | return false; | ||
37 | } | ||
38 | return true; | ||
39 | } | ||
40 | |||
41 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java new file mode 100644 index 00000000..f66f1715 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.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.rete.single; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
19 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | |||
22 | /** | ||
23 | * This node implements a simple filter. A stateless abstract check() predicate determines whether a matching is allowed | ||
24 | * to pass. | ||
25 | * | ||
26 | * @author Gabor Bergmann | ||
27 | * | ||
28 | */ | ||
29 | public abstract class FilterNode extends SingleInputNode { | ||
30 | |||
31 | public FilterNode(final ReteContainer reteContainer) { | ||
32 | super(reteContainer); | ||
33 | } | ||
34 | |||
35 | /** | ||
36 | * Abstract filtering predicate. Expected to be stateless. | ||
37 | * | ||
38 | * @param ps | ||
39 | * the matching to be checked. | ||
40 | * @return true if and only if the parameter matching is allowed to pass through this node. | ||
41 | */ | ||
42 | public abstract boolean check(final Tuple ps); | ||
43 | |||
44 | @Override | ||
45 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
46 | for (final Tuple ps : this.reteContainer.pullPropagatedContents(this, flush)) { | ||
47 | if (check(ps)) { | ||
48 | collector.add(ps); | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) { | ||
55 | for (final Entry<Tuple, Timeline<Timestamp>> entry : this.reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) { | ||
56 | if (check(entry.getKey())) { | ||
57 | collector.put(entry.getKey(), entry.getValue()); | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { | ||
64 | if (check(updateElement)) { | ||
65 | propagateUpdate(direction, updateElement, timestamp); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java new file mode 100644 index 00000000..8dd3e949 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java | |||
@@ -0,0 +1,52 @@ | |||
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.rete.single; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
14 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
15 | |||
16 | /** | ||
17 | * This node filters patterns according to equalities and inequalities of elements. The 'subject' element is asserted to | ||
18 | * be different from the elements given by the inequalityMask. | ||
19 | * | ||
20 | * | ||
21 | * @author Gabor Bergmann | ||
22 | * | ||
23 | */ | ||
24 | public class InequalityFilterNode extends FilterNode { | ||
25 | |||
26 | int subjectIndex; | ||
27 | TupleMask inequalityMask; | ||
28 | |||
29 | /** | ||
30 | * @param reteContainer | ||
31 | * @param subject | ||
32 | * the index of the element that should be compared. | ||
33 | * @param inequalityMask | ||
34 | * the indices of elements that should be different from the subjectIndex. | ||
35 | */ | ||
36 | public InequalityFilterNode(ReteContainer reteContainer, int subject, TupleMask inequalityMask) { | ||
37 | super(reteContainer); | ||
38 | this.subjectIndex = subject; | ||
39 | this.inequalityMask = inequalityMask; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public boolean check(Tuple ps) { | ||
44 | Object subject = ps.get(subjectIndex); | ||
45 | for (int ineq : inequalityMask.indices) { | ||
46 | if (subject.equals(ps.get(ineq))) | ||
47 | return false; | ||
48 | } | ||
49 | return true; | ||
50 | } | ||
51 | |||
52 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java new file mode 100644 index 00000000..95018c4f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java | |||
@@ -0,0 +1,125 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * SPDX-License-Identifier: EPL-2.0 | ||
8 | *******************************************************************************/ | ||
9 | package tools.refinery.viatra.runtime.rete.single; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeElectionAlgorithm; | ||
12 | import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeObserver; | ||
13 | import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; | ||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
19 | import tools.refinery.viatra.runtime.rete.network.ReinitializedNode; | ||
20 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
21 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
22 | |||
23 | import java.util.Collection; | ||
24 | import java.util.Map; | ||
25 | |||
26 | public class RepresentativeElectionNode extends SingleInputNode implements Clearable, RepresentativeObserver, | ||
27 | ReinitializedNode { | ||
28 | private final RepresentativeElectionAlgorithm.Factory algorithmFactory; | ||
29 | private Graph<Object> graph; | ||
30 | private RepresentativeElectionAlgorithm algorithm; | ||
31 | |||
32 | public RepresentativeElectionNode(ReteContainer reteContainer, | ||
33 | RepresentativeElectionAlgorithm.Factory algorithmFactory) { | ||
34 | super(reteContainer); | ||
35 | this.algorithmFactory = algorithmFactory; | ||
36 | graph = new Graph<>(); | ||
37 | algorithm = algorithmFactory.create(graph); | ||
38 | algorithm.setObserver(this); | ||
39 | reteContainer.registerClearable(this); | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public void networkStructureChanged() { | ||
44 | if (reteContainer.isTimelyEvaluation() && reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
45 | throw new IllegalStateException(this + " cannot be used in recursive differential dataflow evaluation!"); | ||
46 | } | ||
47 | super.networkStructureChanged(); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void reinitializeWith(Collection<Tuple> tuples) { | ||
52 | algorithm.dispose(); | ||
53 | graph = new Graph<>(); | ||
54 | for (var tuple : tuples) { | ||
55 | insertEdge(tuple.get(0), tuple.get(1)); | ||
56 | } | ||
57 | algorithm = algorithmFactory.create(graph); | ||
58 | algorithm.setObserver(this); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | public void tupleChanged(Object source, Object representative, Direction direction) { | ||
63 | var tuple = Tuples.staticArityFlatTupleOf(source, representative); | ||
64 | propagateUpdate(direction, tuple, Timestamp.ZERO); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public void clear() { | ||
69 | algorithm.dispose(); | ||
70 | graph = new Graph<>(); | ||
71 | algorithm = algorithmFactory.create(graph); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { | ||
76 | var source = updateElement.get(0); | ||
77 | var target = updateElement.get(1); | ||
78 | switch (direction) { | ||
79 | case INSERT -> insertEdge(source, target); | ||
80 | case DELETE -> deleteEdge(source, target); | ||
81 | default -> throw new IllegalArgumentException("Unknown direction: " + direction); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | private void insertEdge(Object source, Object target) { | ||
86 | graph.insertNode(source); | ||
87 | graph.insertNode(target); | ||
88 | graph.insertEdge(source, target); | ||
89 | } | ||
90 | |||
91 | private void deleteEdge(Object source, Object target) { | ||
92 | graph.deleteEdgeIfExists(source, target); | ||
93 | if (isIsolated(source)) { | ||
94 | graph.deleteNode(source); | ||
95 | } | ||
96 | if (!source.equals(target) && isIsolated(target)) { | ||
97 | graph.deleteNode(target); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | private boolean isIsolated(Object node) { | ||
102 | return graph.getTargetNodes(node).isEmpty() && graph.getSourceNodes(node).isEmpty(); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public void pullInto(Collection<Tuple> collector, boolean flush) { | ||
107 | for (var entry : algorithm.getComponents().entrySet()) { | ||
108 | var representative = entry.getKey(); | ||
109 | for (var node : entry.getValue()) { | ||
110 | collector.add(Tuples.staticArityFlatTupleOf(node, representative)); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | @Override | ||
116 | public void pullIntoWithTimeline(Map<Tuple, Timeline<Timestamp>> collector, boolean flush) { | ||
117 | // Use all zero timestamps because this node cannot be used in recursive groups anyway. | ||
118 | for (var entry : algorithm.getComponents().entrySet()) { | ||
119 | var representative = entry.getKey(); | ||
120 | for (var node : entry.getValue()) { | ||
121 | collector.put(Tuples.staticArityFlatTupleOf(node, representative), Timestamp.INSERT_AT_ZERO_TIMELINE); | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java new file mode 100644 index 00000000..99fc45b2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java | |||
@@ -0,0 +1,126 @@ | |||
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.rete.single; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Map; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.StandardNode; | ||
20 | import tools.refinery.viatra.runtime.rete.network.Supplier; | ||
21 | import tools.refinery.viatra.runtime.rete.network.Tunnel; | ||
22 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
24 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
25 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
26 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
27 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
28 | |||
29 | /** | ||
30 | * @author Gabor Bergmann | ||
31 | * | ||
32 | */ | ||
33 | public abstract class SingleInputNode extends StandardNode implements Tunnel { | ||
34 | |||
35 | protected Supplier parent; | ||
36 | /** | ||
37 | * @since 1.6 | ||
38 | */ | ||
39 | protected Mailbox mailbox; | ||
40 | |||
41 | public SingleInputNode(ReteContainer reteContainer) { | ||
42 | super(reteContainer); | ||
43 | mailbox = instantiateMailbox(); | ||
44 | reteContainer.registerClearable(mailbox); | ||
45 | parent = null; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * Instantiates the {@link Mailbox} of this receiver. | ||
50 | * Subclasses may override this method to provide their own mailbox implementation. | ||
51 | * | ||
52 | * @return the mailbox | ||
53 | * @since 2.0 | ||
54 | */ | ||
55 | protected Mailbox instantiateMailbox() { | ||
56 | if (this.reteContainer.isTimelyEvaluation()) { | ||
57 | return new TimelyMailbox(this, this.reteContainer); | ||
58 | } else { | ||
59 | return new BehaviorChangingMailbox(this, this.reteContainer); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public CommunicationTracker getCommunicationTracker() { | ||
65 | return this.reteContainer.getCommunicationTracker(); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public Mailbox getMailbox() { | ||
70 | return this.mailbox; | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public void appendParent(Supplier supplier) { | ||
75 | if (parent == null) | ||
76 | parent = supplier; | ||
77 | else | ||
78 | throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent | ||
79 | + ") and cannot connect to additional parent (" + supplier | ||
80 | + ") as it is not a Uniqueness Enforcer Node. "); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public void removeParent(Supplier supplier) { | ||
85 | if (parent == supplier) | ||
86 | parent = null; | ||
87 | else | ||
88 | throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not " | ||
89 | + supplier); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * To be called by derived classes and ReteContainer. | ||
94 | */ | ||
95 | public void propagatePullInto(final Collection<Tuple> collector, final boolean flush) { | ||
96 | if (parent != null) { | ||
97 | parent.pullInto(collector, flush); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * To be called by derived classes and ReteContainer. | ||
103 | */ | ||
104 | public void propagatePullIntoWithTimestamp(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
105 | if (parent != null) { | ||
106 | parent.pullIntoWithTimeline(collector, flush); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | @Override | ||
111 | public Collection<Supplier> getParents() { | ||
112 | if (parent == null) | ||
113 | return Collections.emptySet(); | ||
114 | else | ||
115 | return Collections.singleton(parent); | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public void assignTraceInfo(TraceInfo traceInfo) { | ||
120 | super.assignTraceInfo(traceInfo); | ||
121 | if (traceInfo.propagateFromStandardNodeToSupplierParent()) | ||
122 | if (parent != null) | ||
123 | parent.acceptPropagatedTraceInfo(traceInfo); | ||
124 | } | ||
125 | |||
126 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java new file mode 100644 index 00000000..82640948 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java | |||
@@ -0,0 +1,63 @@ | |||
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.rete.single; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.Map; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
15 | import tools.refinery.viatra.runtime.rete.network.ProductionNode; | ||
16 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
17 | import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; | ||
18 | import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; | ||
19 | /** | ||
20 | * Differential dataflow implementation of the Production node, based on {@link TimelyUniquenessEnforcerNode}. | ||
21 | * | ||
22 | * @author Tamas Szabo | ||
23 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
24 | * @since 2.3 | ||
25 | */ | ||
26 | public class TimelyProductionNode extends TimelyUniquenessEnforcerNode implements ProductionNode { | ||
27 | |||
28 | protected final Map<String, Integer> posMapping; | ||
29 | |||
30 | public TimelyProductionNode(final ReteContainer reteContainer, final Map<String, Integer> posMapping) { | ||
31 | super(reteContainer, posMapping.size()); | ||
32 | this.posMapping = posMapping; | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | public Map<String, Integer> getPosMapping() { | ||
37 | return this.posMapping; | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Iterator<Tuple> iterator() { | ||
42 | return this.memory.keySet().iterator(); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) { | ||
47 | if (traceInfo.propagateToProductionNodeParentAlso()) { | ||
48 | super.acceptPropagatedTraceInfo(traceInfo); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | @Override | ||
53 | public String toString() { | ||
54 | for (final TraceInfo traceInfo : this.traceInfos) { | ||
55 | if (traceInfo instanceof CompiledQuery) { | ||
56 | final String patternName = ((CompiledQuery) traceInfo).getPatternName(); | ||
57 | return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString()); | ||
58 | } | ||
59 | } | ||
60 | return super.toString(); | ||
61 | } | ||
62 | |||
63 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java new file mode 100644 index 00000000..4c4b4fc0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java | |||
@@ -0,0 +1,161 @@ | |||
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.rete.single; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Map; | ||
13 | import java.util.Map.Entry; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Signed; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
22 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
23 | import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryIdentityIndexer; | ||
24 | import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryNullIndexer; | ||
25 | import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; | ||
26 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
27 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
28 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
29 | import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; | ||
30 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
31 | import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; | ||
32 | |||
33 | /** | ||
34 | * Timely uniqueness enforcer node implementation. | ||
35 | * | ||
36 | * @author Tamas Szabo | ||
37 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
38 | * @noextend This class is not intended to be subclassed by clients. | ||
39 | * @since 2.4 | ||
40 | */ | ||
41 | public class TimelyUniquenessEnforcerNode extends AbstractUniquenessEnforcerNode implements ResumableNode { | ||
42 | |||
43 | protected final TimelyMemory<Timestamp> memory; | ||
44 | /** | ||
45 | * @since 2.4 | ||
46 | */ | ||
47 | protected CommunicationGroup group; | ||
48 | |||
49 | public TimelyUniquenessEnforcerNode(final ReteContainer container, final int tupleWidth) { | ||
50 | super(container, tupleWidth); | ||
51 | this.memory = new TimelyMemory<Timestamp>( | ||
52 | container.getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); | ||
53 | container.registerClearable(this.memory); | ||
54 | this.mailbox = instantiateMailbox(); | ||
55 | container.registerClearable(this.mailbox); | ||
56 | } | ||
57 | |||
58 | protected Mailbox instantiateMailbox() { | ||
59 | return new TimelyMailbox(this, this.reteContainer); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
64 | for (final Tuple tuple : this.memory.getTuplesAtInfinity()) { | ||
65 | collector.add(tuple); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public CommunicationGroup getCurrentGroup() { | ||
71 | return this.group; | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void setCurrentGroup(final CommunicationGroup group) { | ||
76 | this.group = group; | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public Set<Tuple> getTuples() { | ||
81 | return this.memory.getTuplesAtInfinity(); | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * @since 2.4 | ||
86 | */ | ||
87 | @Override | ||
88 | public Timestamp getResumableTimestamp() { | ||
89 | return this.memory.getResumableTimestamp(); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * @since 2.4 | ||
94 | */ | ||
95 | @Override | ||
96 | public void resumeAt(final Timestamp timestamp) { | ||
97 | final Map<Tuple, Diff<Timestamp>> diffMap = this.memory.resumeAt(timestamp); | ||
98 | for (final Entry<Tuple, Diff<Timestamp>> entry : diffMap.entrySet()) { | ||
99 | for (final Signed<Timestamp> signed : entry.getValue()) { | ||
100 | propagate(signed.getDirection(), entry.getKey(), signed.getPayload()); | ||
101 | } | ||
102 | } | ||
103 | final Timestamp nextTimestamp = this.memory.getResumableTimestamp(); | ||
104 | if (nextTimestamp != null) { | ||
105 | this.group.notifyHasMessage(this.mailbox, nextTimestamp); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
111 | Diff<Timestamp> resultDiff = null; | ||
112 | if (direction == Direction.INSERT) { | ||
113 | resultDiff = this.memory.put(update, timestamp); | ||
114 | } else { | ||
115 | try { | ||
116 | resultDiff = this.memory.remove(update, timestamp); | ||
117 | } catch (final IllegalStateException e) { | ||
118 | issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in " | ||
119 | + this.getClass().getName() + " " + this + " for pattern(s) " | ||
120 | + getTraceInfoPatternsEnumerated(), e); | ||
121 | // diff will remain unset in case of the exception, it is time to return | ||
122 | return; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | for (final Signed<Timestamp> signed : resultDiff) { | ||
127 | propagate(signed.getDirection(), update, signed.getPayload()); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | @Override | ||
132 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
133 | collector.putAll(this.memory.asMap()); | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public ProjectionIndexer getNullIndexer() { | ||
138 | if (this.memoryNullIndexer == null) { | ||
139 | this.memoryNullIndexer = new TimelyMemoryNullIndexer(this.reteContainer, this.tupleWidth, this.memory, this, | ||
140 | this, this.specializedListeners); | ||
141 | this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer); | ||
142 | } | ||
143 | return this.memoryNullIndexer; | ||
144 | } | ||
145 | |||
146 | @Override | ||
147 | public ProjectionIndexer getIdentityIndexer() { | ||
148 | if (this.memoryIdentityIndexer == null) { | ||
149 | this.memoryIdentityIndexer = new TimelyMemoryIdentityIndexer(this.reteContainer, this.tupleWidth, | ||
150 | this.memory, this, this, this.specializedListeners); | ||
151 | this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer); | ||
152 | } | ||
153 | return this.memoryIdentityIndexer; | ||
154 | } | ||
155 | |||
156 | @Override | ||
157 | public void networkStructureChanged() { | ||
158 | super.networkStructureChanged(); | ||
159 | } | ||
160 | |||
161 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java new file mode 100644 index 00000000..24750656 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java | |||
@@ -0,0 +1,49 @@ | |||
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.rete.single; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Map.Entry; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
19 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
20 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
21 | |||
22 | public abstract class TransformerNode extends SingleInputNode { | ||
23 | |||
24 | public TransformerNode(final ReteContainer reteContainer) { | ||
25 | super(reteContainer); | ||
26 | } | ||
27 | |||
28 | protected abstract Tuple transform(final Tuple input); | ||
29 | |||
30 | @Override | ||
31 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
32 | for (Tuple ps : reteContainer.pullPropagatedContents(this, flush)) { | ||
33 | collector.add(transform(ps)); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
39 | for (final Entry<Tuple, Timeline<Timestamp>> entry : reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) { | ||
40 | collector.put(transform(entry.getKey()), entry.getValue()); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { | ||
46 | propagateUpdate(direction, transform(updateElement), timestamp); | ||
47 | } | ||
48 | |||
49 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java new file mode 100644 index 00000000..fdda4ef4 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java | |||
@@ -0,0 +1,147 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, 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.rete.single; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg; | ||
12 | import tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple; | ||
13 | import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; | ||
14 | import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource; | ||
15 | import tools.refinery.viatra.runtime.rete.itc.igraph.ITcObserver; | ||
16 | import tools.refinery.viatra.runtime.matchers.tuple.Tuples; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.Clearable; | ||
18 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
19 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
20 | import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; | ||
21 | import tools.refinery.viatra.runtime.rete.network.ReinitializedNode; | ||
22 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
23 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
24 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
25 | |||
26 | import java.util.Collection; | ||
27 | import java.util.Map; | ||
28 | |||
29 | /** | ||
30 | * This class represents a transitive closure node in the Rete net. | ||
31 | * <p> | ||
32 | * This node must not be used in recursive {@link CommunicationGroup}s. | ||
33 | * | ||
34 | * @author Gabor Bergmann | ||
35 | * | ||
36 | */ | ||
37 | public class TransitiveClosureNode extends SingleInputNode | ||
38 | implements Clearable, ITcObserver<Object>, NetworkStructureChangeSensitiveNode, ReinitializedNode { | ||
39 | |||
40 | private Graph<Object> graphDataSource; | ||
41 | private ITcDataSource<Object> transitiveClosureAlgorithm; | ||
42 | |||
43 | /** | ||
44 | * Create a new transitive closure rete node. | ||
45 | * | ||
46 | * Client may optionally call {@link #reinitializeWith(Collection)} before using the node, instead of inserting the | ||
47 | * initial set of tuples one by one. | ||
48 | * | ||
49 | * @param reteContainer | ||
50 | * the rete container of the node | ||
51 | */ | ||
52 | public TransitiveClosureNode(ReteContainer reteContainer) { | ||
53 | super(reteContainer); | ||
54 | graphDataSource = new Graph<Object>(); | ||
55 | transitiveClosureAlgorithm = new IncSCCAlg<Object>(graphDataSource); | ||
56 | transitiveClosureAlgorithm.attachObserver(this); | ||
57 | reteContainer.registerClearable(this); | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public void networkStructureChanged() { | ||
62 | if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { | ||
63 | throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!"); | ||
64 | } | ||
65 | super.networkStructureChanged(); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Initializes the graph data source with the given collection of tuples. | ||
70 | * | ||
71 | * @param tuples | ||
72 | * the initial collection of tuples | ||
73 | */ | ||
74 | @Override | ||
75 | public void reinitializeWith(Collection<tools.refinery.viatra.runtime.matchers.tuple.Tuple> tuples) { | ||
76 | clear(); | ||
77 | |||
78 | for (tools.refinery.viatra.runtime.matchers.tuple.Tuple t : tuples) { | ||
79 | graphDataSource.insertNode(t.get(0)); | ||
80 | graphDataSource.insertNode(t.get(1)); | ||
81 | graphDataSource.insertEdge(t.get(0), t.get(1)); | ||
82 | } | ||
83 | transitiveClosureAlgorithm.attachObserver(this); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public void pullInto(final Collection<tools.refinery.viatra.runtime.matchers.tuple.Tuple> collector, final boolean flush) { | ||
88 | for (final Tuple<Object> tuple : ((IncSCCAlg<Object>) transitiveClosureAlgorithm).getTcRelation()) { | ||
89 | collector.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget())); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | @Override | ||
94 | public void pullIntoWithTimeline( | ||
95 | final Map<tools.refinery.viatra.runtime.matchers.tuple.Tuple, Timeline<Timestamp>> collector, | ||
96 | final boolean flush) { | ||
97 | // use all zero timestamps because this node cannot be used in recursive groups anyway | ||
98 | for (final Tuple<Object> tuple : ((IncSCCAlg<Object>) transitiveClosureAlgorithm).getTcRelation()) { | ||
99 | collector.put(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()), Timestamp.INSERT_AT_ZERO_TIMELINE); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | @Override | ||
104 | public void update(Direction direction, tools.refinery.viatra.runtime.matchers.tuple.Tuple updateElement, | ||
105 | Timestamp timestamp) { | ||
106 | if (updateElement.getSize() == 2) { | ||
107 | Object source = updateElement.get(0); | ||
108 | Object target = updateElement.get(1); | ||
109 | |||
110 | if (direction == Direction.INSERT) { | ||
111 | graphDataSource.insertNode(source); | ||
112 | graphDataSource.insertNode(target); | ||
113 | graphDataSource.insertEdge(source, target); | ||
114 | } | ||
115 | if (direction == Direction.DELETE) { | ||
116 | graphDataSource.deleteEdgeIfExists(source, target); | ||
117 | |||
118 | if (((IncSCCAlg<Object>) transitiveClosureAlgorithm).isIsolated(source)) { | ||
119 | graphDataSource.deleteNode(source); | ||
120 | } | ||
121 | if (!source.equals(target) && ((IncSCCAlg<Object>) transitiveClosureAlgorithm).isIsolated(target)) { | ||
122 | graphDataSource.deleteNode(target); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public void clear() { | ||
130 | transitiveClosureAlgorithm.dispose(); | ||
131 | graphDataSource = new Graph<Object>(); | ||
132 | transitiveClosureAlgorithm = new IncSCCAlg<Object>(graphDataSource); | ||
133 | } | ||
134 | |||
135 | @Override | ||
136 | public void tupleInserted(Object source, Object target) { | ||
137 | tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target); | ||
138 | propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO); | ||
139 | } | ||
140 | |||
141 | @Override | ||
142 | public void tupleDeleted(Object source, Object target) { | ||
143 | tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target); | ||
144 | propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO); | ||
145 | } | ||
146 | |||
147 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java new file mode 100644 index 00000000..6c21a966 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java | |||
@@ -0,0 +1,48 @@ | |||
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.rete.single; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | |||
15 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
16 | import tools.refinery.viatra.runtime.matchers.util.Direction; | ||
17 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
18 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
19 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
20 | |||
21 | /** | ||
22 | * Simply propagates everything. Might be used to join or fork. | ||
23 | * | ||
24 | * @author Gabor Bergmann | ||
25 | */ | ||
26 | public class TransparentNode extends SingleInputNode { | ||
27 | |||
28 | public TransparentNode(final ReteContainer reteContainer) { | ||
29 | super(reteContainer); | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { | ||
34 | propagateUpdate(direction, updateElement, timestamp); | ||
35 | |||
36 | } | ||
37 | |||
38 | @Override | ||
39 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
40 | propagatePullInto(collector, flush); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
45 | propagatePullIntoWithTimestamp(collector, flush); | ||
46 | } | ||
47 | |||
48 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java new file mode 100644 index 00000000..8a72138c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java | |||
@@ -0,0 +1,61 @@ | |||
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.rete.single; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; | ||
14 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
15 | |||
16 | /** | ||
17 | * Trims the matchings as specified by a mask. | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public class TrimmerNode extends TransformerNode { | ||
23 | |||
24 | protected TupleMask mask; | ||
25 | |||
26 | /** | ||
27 | * @param reteContainer | ||
28 | * @param mask | ||
29 | * The mask used to trim substitutions. | ||
30 | */ | ||
31 | public TrimmerNode(ReteContainer reteContainer, TupleMask mask) { | ||
32 | super(reteContainer); | ||
33 | this.mask = mask; | ||
34 | } | ||
35 | |||
36 | public TrimmerNode(ReteContainer reteContainer) { | ||
37 | super(reteContainer); | ||
38 | this.mask = null; | ||
39 | } | ||
40 | |||
41 | /** | ||
42 | * @return the mask | ||
43 | */ | ||
44 | public TupleMask getMask() { | ||
45 | return mask; | ||
46 | } | ||
47 | |||
48 | /** | ||
49 | * @param mask | ||
50 | * the mask to set | ||
51 | */ | ||
52 | public void setMask(TupleMask mask) { | ||
53 | this.mask = mask; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | protected Tuple transform(Tuple input) { | ||
58 | return mask.transform(input); | ||
59 | } | ||
60 | |||
61 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java new file mode 100644 index 00000000..5bfde248 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java | |||
@@ -0,0 +1,321 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Gabor Bergmann, Tamas Szabo 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.rete.single; | ||
11 | |||
12 | import java.util.Collection; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | |||
16 | import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; | ||
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.Direction; | ||
21 | import tools.refinery.viatra.runtime.matchers.util.IMultiset; | ||
22 | import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; | ||
23 | import tools.refinery.viatra.runtime.rete.index.MemoryIdentityIndexer; | ||
24 | import tools.refinery.viatra.runtime.rete.index.MemoryNullIndexer; | ||
25 | import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; | ||
26 | import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver; | ||
27 | import tools.refinery.viatra.runtime.rete.network.RederivableNode; | ||
28 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
29 | import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; | ||
30 | import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; | ||
31 | import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup; | ||
32 | import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; | ||
33 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; | ||
34 | import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox; | ||
35 | |||
36 | /** | ||
37 | * Timeless uniqueness enforcer node implementation. | ||
38 | * <p> | ||
39 | * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the | ||
40 | * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached | ||
41 | * during the evaluation. | ||
42 | * | ||
43 | * @author Gabor Bergmann | ||
44 | * @author Tamas Szabo | ||
45 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
46 | * @noextend This class is not intended to be subclassed by clients. | ||
47 | */ | ||
48 | public class UniquenessEnforcerNode extends AbstractUniquenessEnforcerNode | ||
49 | implements RederivableNode, PosetAwareReceiver { | ||
50 | |||
51 | protected IMultiset<Tuple> memory; | ||
52 | /** | ||
53 | * @since 1.6 | ||
54 | */ | ||
55 | protected IMultiset<Tuple> rederivableMemory; | ||
56 | /** | ||
57 | * @since 1.6 | ||
58 | */ | ||
59 | protected boolean deleteRederiveEvaluation; | ||
60 | |||
61 | /** | ||
62 | * @since 1.7 | ||
63 | */ | ||
64 | protected CommunicationGroup currentGroup; | ||
65 | |||
66 | public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) { | ||
67 | this(reteContainer, tupleWidth, false); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * OPTIONAL ELEMENT - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE | ||
72 | * | ||
73 | * @since 1.6 | ||
74 | */ | ||
75 | protected final TupleMask coreMask; | ||
76 | /** | ||
77 | * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE | ||
78 | * | ||
79 | * @since 1.6 | ||
80 | */ | ||
81 | protected final TupleMask posetMask; | ||
82 | /** | ||
83 | * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE | ||
84 | * | ||
85 | * @since 1.6 | ||
86 | */ | ||
87 | protected final IPosetComparator posetComparator; | ||
88 | |||
89 | /** | ||
90 | * @since 1.6 | ||
91 | */ | ||
92 | public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth, | ||
93 | final boolean deleteRederiveEvaluation) { | ||
94 | this(reteContainer, tupleWidth, deleteRederiveEvaluation, null, null, null); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * @since 1.6 | ||
99 | */ | ||
100 | public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth, | ||
101 | final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask, | ||
102 | final IPosetComparator posetComparator) { | ||
103 | super(reteContainer, tupleWidth); | ||
104 | this.memory = CollectionsFactory.createMultiset(); | ||
105 | this.rederivableMemory = CollectionsFactory.createMultiset(); | ||
106 | reteContainer.registerClearable(this.memory); | ||
107 | reteContainer.registerClearable(this.rederivableMemory); | ||
108 | this.deleteRederiveEvaluation = deleteRederiveEvaluation; | ||
109 | this.coreMask = coreMask; | ||
110 | this.posetMask = posetMask; | ||
111 | this.posetComparator = posetComparator; | ||
112 | this.mailbox = instantiateMailbox(); | ||
113 | reteContainer.registerClearable(this.mailbox); | ||
114 | } | ||
115 | |||
116 | @Override | ||
117 | public void pullInto(final Collection<Tuple> collector, final boolean flush) { | ||
118 | for (final Tuple tuple : this.memory.distinctValues()) { | ||
119 | collector.add(tuple); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * @since 2.8 | ||
125 | */ | ||
126 | @Override | ||
127 | public Set<Tuple> getTuples() { | ||
128 | return this.memory.distinctValues(); | ||
129 | } | ||
130 | |||
131 | @Override | ||
132 | public boolean isInDRedMode() { | ||
133 | return this.deleteRederiveEvaluation; | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public TupleMask getCoreMask() { | ||
138 | return coreMask; | ||
139 | } | ||
140 | |||
141 | @Override | ||
142 | public TupleMask getPosetMask() { | ||
143 | return posetMask; | ||
144 | } | ||
145 | |||
146 | @Override | ||
147 | public IPosetComparator getPosetComparator() { | ||
148 | return posetComparator; | ||
149 | } | ||
150 | |||
151 | @Override | ||
152 | public void pullIntoWithTimeline(final Map<Tuple, Timeline<Timestamp>> collector, final boolean flush) { | ||
153 | throw new UnsupportedOperationException("Use the timely version of this node!"); | ||
154 | } | ||
155 | |||
156 | /** | ||
157 | * @since 2.0 | ||
158 | */ | ||
159 | protected Mailbox instantiateMailbox() { | ||
160 | if (coreMask != null && posetMask != null && posetComparator != null) { | ||
161 | return new PosetAwareMailbox(this, this.reteContainer); | ||
162 | } else { | ||
163 | return new BehaviorChangingMailbox(this, this.reteContainer); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | @Override | ||
168 | public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { | ||
169 | updateWithPosetInfo(direction, update, false); | ||
170 | } | ||
171 | |||
172 | @Override | ||
173 | public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) { | ||
174 | if (this.deleteRederiveEvaluation) { | ||
175 | if (updateWithDeleteAndRederive(direction, update, monotone)) { | ||
176 | propagate(direction, update, Timestamp.ZERO); | ||
177 | } | ||
178 | } else { | ||
179 | if (updateDefault(direction, update)) { | ||
180 | propagate(direction, update, Timestamp.ZERO); | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * @since 2.4 | ||
187 | */ | ||
188 | protected boolean updateWithDeleteAndRederive(final Direction direction, final Tuple update, | ||
189 | final boolean monotone) { | ||
190 | boolean propagate = false; | ||
191 | |||
192 | final int memoryCount = memory.getCount(update); | ||
193 | final int rederivableCount = rederivableMemory.getCount(update); | ||
194 | |||
195 | if (direction == Direction.INSERT) { | ||
196 | // INSERT | ||
197 | if (rederivableCount != 0) { | ||
198 | // the tuple is in the re-derivable memory | ||
199 | rederivableMemory.addOne(update); | ||
200 | if (rederivableMemory.isEmpty()) { | ||
201 | // there is nothing left to be re-derived | ||
202 | // this can happen if the INSERT cancelled out a DELETE | ||
203 | ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); | ||
204 | } | ||
205 | } else { | ||
206 | // the tuple is in the main memory | ||
207 | propagate = memory.addOne(update); | ||
208 | } | ||
209 | } else { | ||
210 | // DELETE | ||
211 | if (rederivableCount != 0) { | ||
212 | // the tuple is in the re-derivable memory | ||
213 | if (memoryCount != 0) { | ||
214 | issueError("[INTERNAL ERROR] Inconsistent state for " + update | ||
215 | + " because it is present both in the main and re-derivable memory in the UniquenessEnforcerNode " | ||
216 | + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null); | ||
217 | } | ||
218 | |||
219 | try { | ||
220 | rederivableMemory.removeOne(update); | ||
221 | } catch (final IllegalStateException ex) { | ||
222 | issueError( | ||
223 | "[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in UniquenessEnforcer " | ||
224 | + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), | ||
225 | ex); | ||
226 | } | ||
227 | if (rederivableMemory.isEmpty()) { | ||
228 | // there is nothing left to be re-derived | ||
229 | ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); | ||
230 | } | ||
231 | } else { | ||
232 | // the tuple is in the main memory | ||
233 | if (monotone) { | ||
234 | propagate = memory.removeOne(update); | ||
235 | } else { | ||
236 | final int count = memoryCount - 1; | ||
237 | if (count > 0) { | ||
238 | if (rederivableMemory.isEmpty()) { | ||
239 | // there is now something to be re-derived | ||
240 | ((RecursiveCommunicationGroup) currentGroup).addRederivable(this); | ||
241 | } | ||
242 | rederivableMemory.addPositive(update, count); | ||
243 | } | ||
244 | memory.clearAllOf(update); | ||
245 | propagate = true; | ||
246 | } | ||
247 | } | ||
248 | } | ||
249 | |||
250 | return propagate; | ||
251 | } | ||
252 | |||
253 | /** | ||
254 | * @since 2.4 | ||
255 | */ | ||
256 | protected boolean updateDefault(final Direction direction, final Tuple update) { | ||
257 | boolean propagate = false; | ||
258 | if (direction == Direction.INSERT) { | ||
259 | // INSERT | ||
260 | propagate = memory.addOne(update); | ||
261 | } else { | ||
262 | // DELETE | ||
263 | try { | ||
264 | propagate = memory.removeOne(update); | ||
265 | } catch (final IllegalStateException ex) { | ||
266 | propagate = false; | ||
267 | issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in " | ||
268 | + this.getClass().getName() + " " + this + " for pattern(s) " | ||
269 | + getTraceInfoPatternsEnumerated(), ex); | ||
270 | } | ||
271 | } | ||
272 | return propagate; | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * @since 1.6 | ||
277 | */ | ||
278 | @Override | ||
279 | public void rederiveOne() { | ||
280 | final Tuple update = rederivableMemory.iterator().next(); | ||
281 | final int count = rederivableMemory.getCount(update); | ||
282 | rederivableMemory.clearAllOf(update); | ||
283 | memory.addPositive(update, count); | ||
284 | // if there is no other re-derivable tuple, then unregister the node itself | ||
285 | if (this.rederivableMemory.isEmpty()) { | ||
286 | ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); | ||
287 | } | ||
288 | propagate(Direction.INSERT, update, Timestamp.ZERO); | ||
289 | } | ||
290 | |||
291 | @Override | ||
292 | public ProjectionIndexer getNullIndexer() { | ||
293 | if (this.memoryNullIndexer == null) { | ||
294 | this.memoryNullIndexer = new MemoryNullIndexer(this.reteContainer, this.tupleWidth, | ||
295 | this.memory.distinctValues(), this, this, this.specializedListeners); | ||
296 | this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer); | ||
297 | } | ||
298 | return this.memoryNullIndexer; | ||
299 | } | ||
300 | |||
301 | @Override | ||
302 | public ProjectionIndexer getIdentityIndexer() { | ||
303 | if (this.memoryIdentityIndexer == null) { | ||
304 | this.memoryIdentityIndexer = new MemoryIdentityIndexer(this.reteContainer, this.tupleWidth, | ||
305 | this.memory.distinctValues(), this, this, this.specializedListeners); | ||
306 | this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer); | ||
307 | } | ||
308 | return this.memoryIdentityIndexer; | ||
309 | } | ||
310 | |||
311 | @Override | ||
312 | public CommunicationGroup getCurrentGroup() { | ||
313 | return currentGroup; | ||
314 | } | ||
315 | |||
316 | @Override | ||
317 | public void setCurrentGroup(final CommunicationGroup currentGroup) { | ||
318 | this.currentGroup = currentGroup; | ||
319 | } | ||
320 | |||
321 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java new file mode 100644 index 00000000..c641bf6e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java | |||
@@ -0,0 +1,44 @@ | |||
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.rete.single; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.tuple.Tuple; | ||
13 | import tools.refinery.viatra.runtime.rete.network.ReteContainer; | ||
14 | |||
15 | /** | ||
16 | * A filter node that keeps only those tuples that contain a certain value at a certain position. | ||
17 | * | ||
18 | * @author Bergmann Gabor | ||
19 | * | ||
20 | */ | ||
21 | public class ValueBinderFilterNode extends FilterNode { | ||
22 | |||
23 | int bindingIndex; | ||
24 | Object bindingValue; | ||
25 | |||
26 | /** | ||
27 | * @param reteContainer | ||
28 | * @param bindingIndex | ||
29 | * the position in the tuple that should be bound | ||
30 | * @param bindingValue | ||
31 | * the value to which the tuple has to be bound | ||
32 | */ | ||
33 | public ValueBinderFilterNode(ReteContainer reteContainer, int bindingIndex, Object bindingValue) { | ||
34 | super(reteContainer); | ||
35 | this.bindingIndex = bindingIndex; | ||
36 | this.bindingValue = bindingValue; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public boolean check(Tuple ps) { | ||
41 | return bindingValue.equals(ps.get(bindingIndex)); | ||
42 | } | ||
43 | |||
44 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java new file mode 100644 index 00000000..2055dfe8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java | |||
@@ -0,0 +1,24 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
12 | |||
13 | public class ActiveNodeConflictTrace extends RecipeTraceInfo { // TODO implement PatternTraceInfo | ||
14 | RecipeTraceInfo inactiveRecipeTrace; | ||
15 | public ActiveNodeConflictTrace(ReteNodeRecipe recipe, | ||
16 | RecipeTraceInfo parentRecipeTrace, | ||
17 | RecipeTraceInfo inactiveRecipeTrace) { | ||
18 | super(recipe, parentRecipeTrace); | ||
19 | this.inactiveRecipeTrace = inactiveRecipeTrace; | ||
20 | } | ||
21 | public RecipeTraceInfo getInactiveRecipeTrace() { | ||
22 | return inactiveRecipeTrace; | ||
23 | } | ||
24 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java new file mode 100644 index 00000000..b8c793c5 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java | |||
@@ -0,0 +1,54 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
14 | import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; | ||
15 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
16 | |||
17 | /** | ||
18 | * Indicates that recipe expresses the finished match set of a query. | ||
19 | * @author Bergmann Gabor | ||
20 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
21 | */ | ||
22 | public class CompiledQuery extends RecipeTraceInfo implements | ||
23 | PatternTraceInfo { | ||
24 | |||
25 | private PQuery query; | ||
26 | private final Map<PBody, ? extends RecipeTraceInfo> parentRecipeTracesPerBody; | ||
27 | |||
28 | /** | ||
29 | * @since 1.6 | ||
30 | */ | ||
31 | public CompiledQuery(ReteNodeRecipe recipe, | ||
32 | Map<PBody, ? extends RecipeTraceInfo> parentRecipeTraces, | ||
33 | PQuery query) { | ||
34 | super(recipe, parentRecipeTraces.values()); | ||
35 | parentRecipeTracesPerBody = parentRecipeTraces; | ||
36 | this.query = query; | ||
37 | } | ||
38 | public PQuery getQuery() { | ||
39 | return query; | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public String getPatternName() { | ||
44 | return query.getFullyQualifiedName(); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @since 1.6 | ||
49 | */ | ||
50 | public Map<PBody, ? extends RecipeTraceInfo> getParentRecipeTracesPerBody() { | ||
51 | return parentRecipeTracesPerBody; | ||
52 | } | ||
53 | |||
54 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java new file mode 100644 index 00000000..572e943c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java | |||
@@ -0,0 +1,51 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Set; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
19 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
20 | import tools.refinery.viatra.runtime.matchers.util.Preconditions; | ||
21 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
22 | |||
23 | /** | ||
24 | * A trace marker associating a Rete recipe with a query SubPlan. | ||
25 | * | ||
26 | * <p> The Rete node represented by the recipe is equivalent to the SubPlan. | ||
27 | * <p> Invariant: each variable has at most one index associated with it in the tuple, i.e. no duplicates. | ||
28 | */ | ||
29 | public class CompiledSubPlan extends PlanningTrace { | ||
30 | |||
31 | public CompiledSubPlan(SubPlan subPlan, List<PVariable> variablesTuple, | ||
32 | ReteNodeRecipe recipe, | ||
33 | Collection<? extends RecipeTraceInfo> parentRecipeTraces) { | ||
34 | super(subPlan, variablesTuple, recipe, parentRecipeTraces); | ||
35 | |||
36 | // Make sure that each variable occurs only once | ||
37 | Set<PVariable> variablesSet = new HashSet<PVariable>(variablesTuple); | ||
38 | Preconditions.checkState(variablesSet.size() == variablesTuple.size(), | ||
39 | () -> String.format( | ||
40 | "Illegal column duplication (%s) while the query plan %s was compiled into a Rete Recipe %s", | ||
41 | variablesTuple.stream().map(PVariable::getName).collect(Collectors.joining(",")), | ||
42 | subPlan.toShortString(), recipe)); | ||
43 | } | ||
44 | |||
45 | public CompiledSubPlan(SubPlan subPlan, List<PVariable> variablesTuple, | ||
46 | ReteNodeRecipe recipe, | ||
47 | RecipeTraceInfo... parentRecipeTraces) { | ||
48 | this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces)); | ||
49 | } | ||
50 | |||
51 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java new file mode 100644 index 00000000..8da1e314 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java | |||
@@ -0,0 +1,42 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collection; | ||
13 | |||
14 | import tools.refinery.viatra.runtime.matchers.psystem.PBody; | ||
15 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
16 | |||
17 | /** | ||
18 | * The recipe projects the finished results of a {@link PBody} onto the list of parameters. | ||
19 | * @author Bergmann Gabor | ||
20 | * | ||
21 | */ | ||
22 | public class ParameterProjectionTrace extends RecipeTraceInfo implements PatternTraceInfo { | ||
23 | |||
24 | public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe, | ||
25 | RecipeTraceInfo... parentRecipeTraces) { | ||
26 | this(body, recipe, Arrays.asList(parentRecipeTraces)); | ||
27 | } | ||
28 | |||
29 | public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe, | ||
30 | Collection<? extends RecipeTraceInfo> parentRecipeTraces) { | ||
31 | super(recipe, parentRecipeTraces); | ||
32 | this.body = body; | ||
33 | } | ||
34 | |||
35 | PBody body; | ||
36 | |||
37 | @Override | ||
38 | public String getPatternName() { | ||
39 | return body.getPattern().getFullyQualifiedName(); | ||
40 | } | ||
41 | |||
42 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java new file mode 100644 index 00000000..fb7ef062 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java | |||
@@ -0,0 +1,17 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | /** | ||
12 | * One kind of trace marker that merely establishes the pattern for which the node was built. | ||
13 | * @author Bergmann Gabor | ||
14 | */ | ||
15 | public interface PatternTraceInfo extends TraceInfo { | ||
16 | String getPatternName(); | ||
17 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java new file mode 100644 index 00000000..c1cc3a69 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java | |||
@@ -0,0 +1,80 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.matchers.planning.SubPlan; | ||
18 | import tools.refinery.viatra.runtime.matchers.psystem.PVariable; | ||
19 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
20 | |||
21 | /** | ||
22 | * A trace marker associating a Rete recipe with a query SubPlan. | ||
23 | * | ||
24 | * <p> The recipe may be an auxiliary node; | ||
25 | * see {@link CompiledSubPlan} if it represents the entire SubPlan instead. | ||
26 | */ | ||
27 | public class PlanningTrace extends RecipeTraceInfo implements PatternTraceInfo { | ||
28 | |||
29 | protected SubPlan subPlan; | ||
30 | protected List<PVariable> variablesTuple; | ||
31 | protected Map<PVariable, Integer> posMapping; | ||
32 | |||
33 | public PlanningTrace(SubPlan subPlan, List<PVariable> variablesTuple, | ||
34 | ReteNodeRecipe recipe, | ||
35 | Collection<? extends RecipeTraceInfo> parentRecipeTraces) { | ||
36 | super(recipe, parentRecipeTraces); | ||
37 | this.subPlan = subPlan; | ||
38 | this.variablesTuple = variablesTuple; | ||
39 | |||
40 | this.posMapping = new HashMap<PVariable, Integer>(); | ||
41 | for (int i = 0; i < variablesTuple.size(); ++i) | ||
42 | posMapping.put(variablesTuple.get(i), i); | ||
43 | } | ||
44 | |||
45 | public PlanningTrace(SubPlan subPlan, List<PVariable> variablesTuple, | ||
46 | ReteNodeRecipe recipe, | ||
47 | RecipeTraceInfo... parentRecipeTraces) { | ||
48 | this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces)); | ||
49 | } | ||
50 | |||
51 | public SubPlan getSubPlan() { | ||
52 | return subPlan; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public String getPatternName() { | ||
57 | return subPlan.getBody().getPattern().getFullyQualifiedName(); | ||
58 | } | ||
59 | |||
60 | public List<PVariable> getVariablesTuple() { | ||
61 | return variablesTuple; | ||
62 | } | ||
63 | |||
64 | public Map<PVariable, Integer> getPosMapping() { | ||
65 | return posMapping; | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * Returns a new clone that reinterprets the same compiled form | ||
70 | * as the compiled form of a (potentially different) subPlan. | ||
71 | * Useful e.g. if child plan turns out to be a no-op, or when promoting a {@link PlanningTrace} to {@link CompiledSubPlan}. | ||
72 | */ | ||
73 | public CompiledSubPlan cloneFor(SubPlan newSubPlan) { | ||
74 | return new CompiledSubPlan(newSubPlan, | ||
75 | getVariablesTuple(), | ||
76 | getRecipe(), | ||
77 | getParentRecipeTracesForCloning()); | ||
78 | } | ||
79 | |||
80 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java new file mode 100644 index 00000000..8f610550 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java | |||
@@ -0,0 +1,81 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Arrays; | ||
13 | import java.util.Collection; | ||
14 | import java.util.Collections; | ||
15 | import java.util.List; | ||
16 | |||
17 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
18 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
19 | |||
20 | /** | ||
21 | * A trace marker that indicates the recipe for which the node was built. | ||
22 | * @author Bergmann Gabor | ||
23 | */ | ||
24 | public class RecipeTraceInfo implements TraceInfo { | ||
25 | public ReteNodeRecipe getRecipe() {return recipe;} | ||
26 | /** | ||
27 | * For cloning in case of recursion cut-off points, use {@link #getParentRecipeTracesForCloning()} instead. | ||
28 | * @return an unmodifiable view on parent traces, to be constructed before this node (or alongside, in case of recursion) | ||
29 | */ | ||
30 | public List<RecipeTraceInfo> getParentRecipeTraces() {return Collections.unmodifiableList(new ArrayList<>(parentRecipeTraces));} | ||
31 | /** | ||
32 | * Directly return the underlying collection so that changes to it will be transparent. Use only for recursion-tolerant cloning. | ||
33 | * @noreference This method is not intended to be referenced by clients. | ||
34 | */ | ||
35 | public Collection<? extends RecipeTraceInfo> getParentRecipeTracesForCloning() {return parentRecipeTraces;} | ||
36 | @Override | ||
37 | public Node getNode() {return node;} | ||
38 | |||
39 | private Node node; | ||
40 | ReteNodeRecipe recipe; | ||
41 | ReteNodeRecipe shadowedRecipe; | ||
42 | Collection<? extends RecipeTraceInfo> parentRecipeTraces; | ||
43 | |||
44 | |||
45 | public RecipeTraceInfo(ReteNodeRecipe recipe, Collection<? extends RecipeTraceInfo> parentRecipeTraces) { | ||
46 | super(); | ||
47 | this.recipe = recipe; | ||
48 | this.parentRecipeTraces = parentRecipeTraces; //ParentTraceList.from(parentRecipeTraces); | ||
49 | } | ||
50 | public RecipeTraceInfo(ReteNodeRecipe recipe, RecipeTraceInfo... parentRecipeTraces) { | ||
51 | this(recipe, Arrays.asList(parentRecipeTraces)); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public boolean propagateToIndexerParent() {return false;} | ||
56 | @Override | ||
57 | public boolean propagateFromIndexerToSupplierParent() {return false;} | ||
58 | @Override | ||
59 | public boolean propagateFromStandardNodeToSupplierParent() {return false;} | ||
60 | @Override | ||
61 | public boolean propagateToProductionNodeParentAlso() {return false;} | ||
62 | @Override | ||
63 | public void assignNode(Node node) {this.node = node;} | ||
64 | |||
65 | /** | ||
66 | * @param knownRecipe a known recipe that is equivalent to the current recipe | ||
67 | */ | ||
68 | public void shadowWithEquivalentRecipe(ReteNodeRecipe knownRecipe) { | ||
69 | this.shadowedRecipe = this.recipe; | ||
70 | this.recipe = knownRecipe; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * Get original recipe shadowed by an equivalent | ||
75 | */ | ||
76 | public ReteNodeRecipe getShadowedRecipe() { | ||
77 | return shadowedRecipe; | ||
78 | } | ||
79 | |||
80 | |||
81 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java new file mode 100644 index 00000000..e1d440db --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.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.rete.traceability; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.rete.network.Node; | ||
12 | |||
13 | |||
14 | /** | ||
15 | * Traces the node back to a purpose for which the node was built, | ||
16 | * to explain why the node is there and what it means. | ||
17 | * @author Bergmann Gabor | ||
18 | */ | ||
19 | public interface TraceInfo { | ||
20 | boolean propagateToIndexerParent(); | ||
21 | boolean propagateFromIndexerToSupplierParent(); | ||
22 | boolean propagateFromStandardNodeToSupplierParent(); | ||
23 | boolean propagateToProductionNodeParentAlso(); | ||
24 | |||
25 | void assignNode(Node node); | ||
26 | Node getNode(); | ||
27 | } | ||
28 | // /** | ||
29 | // * The semantics of the tuples contained in this node. | ||
30 | // * @return a tuple of correct size representing the semantics of each position. | ||
31 | // * @post not null | ||
32 | // */ | ||
33 | // Tuple getSemantics(); \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java new file mode 100644 index 00000000..11e4db32 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java | |||
@@ -0,0 +1,36 @@ | |||
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.rete.traceability; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | |||
13 | import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; | ||
14 | |||
15 | // private class AggregatorReferenceIndexTraceInfo extends RecipeTraceInfo { | ||
16 | // RecipeTraceInfo aggregatorNodeRecipeTrace; | ||
17 | // public AggregatorReferenceIndexTraceInfo(ProjectionIndexerRecipe recipe, | ||
18 | // RecipeTraceInfo parentRecipeTrace, | ||
19 | // RecipeTraceInfo aggregatorNodeRecipeTrace) { | ||
20 | // super(recipe, parentRecipeTrace); | ||
21 | // this.aggregatorNodeRecipeTrace = aggregatorNodeRecipeTrace; | ||
22 | // } | ||
23 | // public RecipeTraceInfo getAggregatorNodeRecipeTrace() { | ||
24 | // return aggregatorNodeRecipeTrace; | ||
25 | // } | ||
26 | // } | ||
27 | public class UserRequestTrace extends RecipeTraceInfo { | ||
28 | public UserRequestTrace(ReteNodeRecipe recipe, | ||
29 | Collection<RecipeTraceInfo> parentRecipeTraces) { | ||
30 | super(recipe, parentRecipeTraces); | ||
31 | } | ||
32 | public UserRequestTrace(ReteNodeRecipe recipe, | ||
33 | RecipeTraceInfo... parentRecipeTraces) { | ||
34 | super(recipe, parentRecipeTraces); | ||
35 | } | ||
36 | } \ No newline at end of file | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java new file mode 100644 index 00000000..0efc50af --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java | |||
@@ -0,0 +1,58 @@ | |||
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.rete.util; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | import java.util.Iterator; | ||
13 | |||
14 | /** | ||
15 | * A comparator that compares two iterables based on the lexicographic sorting induced by a comparator on elements. | ||
16 | * @author Bergmann Gabor | ||
17 | * | ||
18 | */ | ||
19 | public class LexicographicComparator<T> implements Comparator<Iterable<? extends T>> { | ||
20 | |||
21 | final Comparator<T> elementComparator; | ||
22 | |||
23 | public LexicographicComparator(Comparator<T> elementComparator) { | ||
24 | super(); | ||
25 | this.elementComparator = elementComparator; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public int compare(Iterable<? extends T> o1, Iterable<? extends T> o2) { | ||
30 | Iterator<? extends T> it1 = o1.iterator(); | ||
31 | Iterator<? extends T> it2 = o2.iterator(); | ||
32 | |||
33 | boolean has1, has2, bothHaveNext; | ||
34 | do { | ||
35 | has1 = it1.hasNext(); | ||
36 | has2 = it2.hasNext(); | ||
37 | bothHaveNext = has1 && has2; | ||
38 | if (bothHaveNext) { | ||
39 | T element1 = it1.next(); | ||
40 | T element2 = it2.next(); | ||
41 | int elementComparison = elementComparator.compare(element1, element2); | ||
42 | if (elementComparison != 0) | ||
43 | return elementComparison; | ||
44 | } | ||
45 | } while (bothHaveNext); | ||
46 | if (has1 && !has2) { | ||
47 | return +1; | ||
48 | } else if (!has1 && has2) { | ||
49 | return -1; | ||
50 | } else /*if (!has1 && !has2)*/ { | ||
51 | return 0; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | |||
56 | |||
57 | |||
58 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java new file mode 100644 index 00000000..96cc445f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java | |||
@@ -0,0 +1,111 @@ | |||
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.rete.util; | ||
11 | |||
12 | import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; | ||
13 | import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; | ||
14 | import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy; | ||
15 | import tools.refinery.viatra.runtime.rete.construction.basiclinear.BasicLinearLayout; | ||
16 | import tools.refinery.viatra.runtime.rete.construction.quasitree.QuasiTreeLayout; | ||
17 | import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationGroup; | ||
18 | |||
19 | /** | ||
20 | * Feature switches. | ||
21 | * @author Gabor Bergmann | ||
22 | * @noreference | ||
23 | */ | ||
24 | public class Options { | ||
25 | |||
26 | public enum NodeSharingOption { | ||
27 | NEVER, // not recommended, patternmatcher leaks possible | ||
28 | INDEXER_AND_REMOTEPROXY, ALL | ||
29 | } | ||
30 | |||
31 | public static final NodeSharingOption nodeSharingOption = NodeSharingOption.ALL; | ||
32 | public static final boolean releaseOnetimeIndexers = true; // effective only | ||
33 | // with | ||
34 | // nodesharing | ||
35 | // ==NEVER | ||
36 | |||
37 | public enum InjectivityStrategy { | ||
38 | EAGER, LAZY | ||
39 | } | ||
40 | |||
41 | public static final InjectivityStrategy injectivityStrategy = InjectivityStrategy.EAGER; | ||
42 | |||
43 | public static final boolean enableInheritance = true; | ||
44 | |||
45 | // public final static boolean useComplementerMask = true; | ||
46 | |||
47 | public static final boolean employTrivialIndexers = true; | ||
48 | |||
49 | // public final static boolean synchronous = false; | ||
50 | |||
51 | public static final int numberOfLocalContainers = 1; | ||
52 | public static final int firstFreeContainer = 0; // 0 if head container is | ||
53 | // free to contain pattern | ||
54 | // bodies, 1 otherwise | ||
55 | |||
56 | /** | ||
57 | * Enable for internal debugging of Rete communication scheme; | ||
58 | * catches cases where the topological sort is violated by a message sent "backwards" | ||
59 | * @since 1.6 | ||
60 | */ | ||
61 | public static final boolean MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING = false; | ||
62 | |||
63 | /** | ||
64 | * Enable for internal debugging of message delivery in {@link TimelyCommunicationGroup}s; | ||
65 | * catches cases when there is a violation of increasing timestamps during message delivery within a group. | ||
66 | * @since 2.3 | ||
67 | */ | ||
68 | public static final boolean MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS = false; | ||
69 | |||
70 | /** | ||
71 | * | ||
72 | * @author Gabor Bergmann | ||
73 | * @noreference | ||
74 | */ | ||
75 | public enum BuilderMethod { | ||
76 | LEGACY, // ONLY with GTASM | ||
77 | PSYSTEM_BASIC_LINEAR, PSYSTEM_QUASITREE; | ||
78 | /** | ||
79 | * @since 1.5 | ||
80 | */ | ||
81 | public IQueryPlannerStrategy layoutStrategy(IQueryBackendContext bContext, IQueryBackendHintProvider hintProvider) { | ||
82 | switch (this) { | ||
83 | case PSYSTEM_BASIC_LINEAR: | ||
84 | return new BasicLinearLayout(bContext); | ||
85 | case PSYSTEM_QUASITREE: | ||
86 | return new QuasiTreeLayout(bContext, hintProvider); | ||
87 | default: | ||
88 | throw new UnsupportedOperationException(); | ||
89 | } | ||
90 | } | ||
91 | } | ||
92 | |||
93 | public static final BuilderMethod builderMethod = | ||
94 | // BuilderMethod.PSYSTEM_BASIC_LINEAR; | ||
95 | BuilderMethod.PSYSTEM_QUASITREE; | ||
96 | |||
97 | public enum FunctionalDependencyOption { | ||
98 | OFF, | ||
99 | OPPORTUNISTIC | ||
100 | } | ||
101 | public static final FunctionalDependencyOption functionalDependencyOption = | ||
102 | FunctionalDependencyOption.OPPORTUNISTIC; | ||
103 | |||
104 | public enum PlanTrimOption { | ||
105 | OFF, | ||
106 | OPPORTUNISTIC | ||
107 | } | ||
108 | public static final PlanTrimOption planTrimOption = | ||
109 | PlanTrimOption.OPPORTUNISTIC; | ||
110 | |||
111 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java new file mode 100644 index 00000000..8b147cf6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java | |||
@@ -0,0 +1,92 @@ | |||
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.rete.util; | ||
11 | |||
12 | import java.util.Comparator; | ||
13 | |||
14 | /** | ||
15 | * Comparing agent for an ordering. Terminology: the "preferred" item will register as LESS. | ||
16 | * | ||
17 | * @author Gabor Bergmann | ||
18 | * | ||
19 | */ | ||
20 | public abstract class OrderingCompareAgent<T> { | ||
21 | protected T a; | ||
22 | protected T b; | ||
23 | |||
24 | /** | ||
25 | * @param a | ||
26 | * @param b | ||
27 | */ | ||
28 | public OrderingCompareAgent(T a, T b) { | ||
29 | super(); | ||
30 | this.a = a; | ||
31 | this.b = b; | ||
32 | } | ||
33 | |||
34 | int result = 0; | ||
35 | |||
36 | protected abstract void doCompare(); | ||
37 | |||
38 | /** | ||
39 | * @return the result | ||
40 | */ | ||
41 | public int compare() { | ||
42 | doCompare(); | ||
43 | return result; | ||
44 | } | ||
45 | |||
46 | // COMPARISON HELPERS | ||
47 | protected boolean isUnknown() { | ||
48 | return result == 0; | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * @pre result == 0 | ||
53 | */ | ||
54 | protected boolean consider(int partial) { | ||
55 | if (isUnknown()) | ||
56 | result = partial; | ||
57 | return isUnknown(); | ||
58 | } | ||
59 | |||
60 | protected boolean swallowBoolean(boolean x) { | ||
61 | return x; | ||
62 | } | ||
63 | |||
64 | // PREFERENCE FUNCTIONS | ||
65 | protected static int dontCare() { | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | protected static int preferTrue(boolean b1, boolean b2) { | ||
70 | return (b1 ^ b2) ? (b1 ? -1 : +1) : 0; | ||
71 | } | ||
72 | |||
73 | protected static int preferFalse(boolean b1, boolean b2) { | ||
74 | return (b1 ^ b2) ? (b2 ? -1 : +1) : 0; | ||
75 | } | ||
76 | |||
77 | protected static <U> int preferLess(Comparable<U> c1, U c2) { | ||
78 | return c1.compareTo(c2); | ||
79 | } | ||
80 | |||
81 | protected static <U> int preferLess(U c1, U c2, Comparator<U> comp) { | ||
82 | return comp.compare(c1, c2); | ||
83 | } | ||
84 | |||
85 | protected static <U> int preferMore(Comparable<U> c1, U c2) { | ||
86 | return -c1.compareTo(c2); | ||
87 | } | ||
88 | protected static <U> int preferMore(U c1, U c2, Comparator<U> comp) { | ||
89 | return -comp.compare(c1, c2); | ||
90 | } | ||
91 | |||
92 | } | ||
diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java new file mode 100644 index 00000000..6e685253 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java | |||
@@ -0,0 +1,60 @@ | |||
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.rete.util; | ||
10 | |||
11 | import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; | ||
12 | import tools.refinery.viatra.runtime.matchers.backend.QueryHintOption; | ||
13 | import tools.refinery.viatra.runtime.rete.matcher.DRedReteBackendFactory; | ||
14 | |||
15 | /** | ||
16 | * Provides key objects (of type {@link QueryHintOption}) for {@link QueryEvaluationHint}s. | ||
17 | * @author Gabor Bergmann | ||
18 | * @since 1.5 | ||
19 | */ | ||
20 | public final class ReteHintOptions { | ||
21 | |||
22 | private ReteHintOptions() {/*Utility class constructor*/} | ||
23 | |||
24 | public static final QueryHintOption<Boolean> useDiscriminatorDispatchersForConstantFiltering = | ||
25 | hintOption("useDiscriminatorDispatchersForConstantFiltering", true); | ||
26 | |||
27 | public static final QueryHintOption<Boolean> prioritizeConstantFiltering = | ||
28 | hintOption("prioritizeConstantFiltering", true); | ||
29 | |||
30 | public static final QueryHintOption<Boolean> cacheOutputOfEvaluatorsByDefault = | ||
31 | hintOption("cacheOutputOfEvaluatorsByDefault", true); | ||
32 | |||
33 | /** | ||
34 | * The incremental query evaluator backend can evaluate recursive patterns. | ||
35 | * However, by default, instance models that contain cycles are not supported with recursive queries | ||
36 | * and can lead to incorrect query results. | ||
37 | * Enabling Delete And Rederive (DRED) mode guarantees that recursive query evaluation leads to correct results in these cases as well. | ||
38 | * | ||
39 | * <p> As DRED may diminish the performance of incremental maintenance, it is not enabled by default. | ||
40 | * @since 1.6 | ||
41 | * @deprecated Use {@link DRedReteBackendFactory} instead of setting this option to true. | ||
42 | */ | ||
43 | @Deprecated | ||
44 | public static final QueryHintOption<Boolean> deleteRederiveEvaluation = | ||
45 | hintOption("deleteRederiveEvaluation", false); | ||
46 | |||
47 | /** | ||
48 | * This hint allows the query planner to take advantage of "weakened alternative" suggestions of the meta context. | ||
49 | * For instance, enumerable unary type constraints may be substituted with a simple type filtering where sufficient. | ||
50 | * | ||
51 | * @since 1.6 | ||
52 | */ | ||
53 | public static final QueryHintOption<Boolean> expandWeakenedAlternativeConstraints = | ||
54 | hintOption("expandWeakenedAlternativeConstraints", true); | ||
55 | |||
56 | // internal helper for conciseness | ||
57 | private static <T> QueryHintOption<T> hintOption(String hintKeyLocalName, T defaultValue) { | ||
58 | return new QueryHintOption<>(ReteHintOptions.class, hintKeyLocalName, defaultValue); | ||
59 | } | ||
60 | } | ||