diff options
Diffstat (limited to 'subprojects/viatra-runtime-rete')
182 files changed, 22806 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-rete/about.html b/subprojects/viatra-runtime-rete/about.html new file mode 100644 index 00000000..d1d5593a --- /dev/null +++ b/subprojects/viatra-runtime-rete/about.html | |||
@@ -0,0 +1,26 @@ | |||
1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> | ||
2 | <html> | ||
3 | <!-- | ||
4 | Copyright (c) 2017, Eclipse.org Foundation, Inc. | ||
5 | |||
6 | SPDX-License-Identifier: LicenseRef-EPL-Steward | ||
7 | --> | ||
8 | <head> | ||
9 | <title>About</title> | ||
10 | <meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1"> | ||
11 | </head> | ||
12 | <body lang="EN-US"> | ||
13 | <h2>About This Content</h2> | ||
14 | |||
15 | <p>March 18, 2019</p> | ||
16 | <h3>License</h3> | ||
17 | |||
18 | <p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the | ||
19 | Eclipse Public License Version 2.0 ("EPL"). A copy of the EPL is available at <a href="http://www.eclipse.org/org/documents/epl-v20.php">http://www.eclipse.org/legal/epl-v20.html</a>. | ||
20 | For purposes of the EPL, "Program" will mean the Content.</p> | ||
21 | |||
22 | <p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may | ||
23 | apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise | ||
24 | indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> | ||
25 | </body> | ||
26 | </html> | ||
diff --git a/subprojects/viatra-runtime-rete/build.gradle.kts b/subprojects/viatra-runtime-rete/build.gradle.kts new file mode 100644 index 00000000..7e795a90 --- /dev/null +++ b/subprojects/viatra-runtime-rete/build.gradle.kts | |||
@@ -0,0 +1,15 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | plugins { | ||
8 | id("tools.refinery.gradle.java-library") | ||
9 | } | ||
10 | |||
11 | dependencies { | ||
12 | implementation(project(":refinery-viatra-runtime")) | ||
13 | implementation(project(":refinery-viatra-runtime-rete-recipes")) | ||
14 | implementation(libs.slf4j.log4j) | ||
15 | } | ||
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 | } | ||