aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/eval/RelationEvaluatorNode.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/eval/RelationEvaluatorNode.java')
-rw-r--r--subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/eval/RelationEvaluatorNode.java183
1 files changed, 183 insertions, 0 deletions
diff --git a/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/eval/RelationEvaluatorNode.java b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/rete/eval/RelationEvaluatorNode.java
new file mode 100644
index 00000000..640ac43b
--- /dev/null
+++ b/subprojects/interpreter-rete/src/main/java/tools/refinery/interpreter/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.interpreter.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.interpreter.rete.misc.SimpleReceiver;
19import tools.refinery.interpreter.rete.network.communication.Timestamp;
20import tools.refinery.interpreter.rete.single.AbstractUniquenessEnforcerNode;
21import tools.refinery.interpreter.matchers.psystem.IRelationEvaluator;
22import tools.refinery.interpreter.matchers.psystem.basicdeferred.RelationEvaluation;
23import tools.refinery.interpreter.matchers.tuple.Tuple;
24import tools.refinery.interpreter.matchers.util.Clearable;
25import tools.refinery.interpreter.matchers.util.Direction;
26import tools.refinery.interpreter.matchers.util.timeline.Timeline;
27import tools.refinery.interpreter.matchers.util.timeline.Timelines;
28import tools.refinery.interpreter.rete.network.ProductionNode;
29import tools.refinery.interpreter.rete.network.Receiver;
30import tools.refinery.interpreter.rete.network.ReteContainer;
31import tools.refinery.interpreter.rete.network.StandardNode;
32import tools.refinery.interpreter.rete.network.Supplier;
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}