diff options
Diffstat (limited to 'subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval')
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 | *******************************************************************************/ | ||
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 | } | ||