aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval')
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java65
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java180
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java25
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java75
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java311
-rw-r--r--subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java183
6 files changed, 839 insertions, 0 deletions
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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.Iterator;
12
13import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
14import tools.refinery.viatra.runtime.matchers.util.Direction;
15import tools.refinery.viatra.runtime.rete.network.ReteContainer;
16import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
17import tools.refinery.viatra.runtime.rete.single.SingleInputNode;
18
19/**
20 * @author Bergmann Gabor
21 */
22public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.Collections;
12import java.util.Iterator;
13import java.util.Map;
14import java.util.Set;
15
16import org.apache.log4j.Logger;
17import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext;
18import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator;
19import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
20import tools.refinery.viatra.runtime.matchers.tuple.TupleValueProvider;
21import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
22import 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 */
31public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import 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 */
18public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.Iterator;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
19import tools.refinery.viatra.runtime.matchers.util.Direction;
20import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
21import tools.refinery.viatra.runtime.rete.network.ReteContainer;
22import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
23
24/**
25 * @author Bergmann Gabor
26 *
27 */
28public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.Collection;
12import java.util.Collections;
13import java.util.Iterator;
14import java.util.Map;
15import java.util.Map.Entry;
16
17import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
18import tools.refinery.viatra.runtime.matchers.tuple.Tuples;
19import tools.refinery.viatra.runtime.matchers.util.Clearable;
20import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory;
21import tools.refinery.viatra.runtime.matchers.util.Direction;
22import tools.refinery.viatra.runtime.matchers.util.Signed;
23import tools.refinery.viatra.runtime.matchers.util.TimelyMemory;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Diff;
25import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
26import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation;
27import tools.refinery.viatra.runtime.rete.network.ReteContainer;
28import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup;
29import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
30import 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 */
39public 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 *******************************************************************************/
9package tools.refinery.viatra.runtime.rete.eval;
10
11import java.util.ArrayList;
12import java.util.Collection;
13import java.util.List;
14import java.util.Map;
15import java.util.Map.Entry;
16import java.util.Set;
17
18import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator;
19import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.RelationEvaluation;
20import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
21import tools.refinery.viatra.runtime.matchers.util.Clearable;
22import tools.refinery.viatra.runtime.matchers.util.Direction;
23import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline;
24import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines;
25import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver;
26import tools.refinery.viatra.runtime.rete.network.ProductionNode;
27import tools.refinery.viatra.runtime.rete.network.Receiver;
28import tools.refinery.viatra.runtime.rete.network.ReteContainer;
29import tools.refinery.viatra.runtime.rete.network.StandardNode;
30import tools.refinery.viatra.runtime.rete.network.Supplier;
31import tools.refinery.viatra.runtime.rete.network.communication.Timestamp;
32import 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 */
42public 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}