diff options
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.java | 183 |
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 | *******************************************************************************/ | ||
9 | package tools.refinery.interpreter.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.interpreter.rete.misc.SimpleReceiver; | ||
19 | import tools.refinery.interpreter.rete.network.communication.Timestamp; | ||
20 | import tools.refinery.interpreter.rete.single.AbstractUniquenessEnforcerNode; | ||
21 | import tools.refinery.interpreter.matchers.psystem.IRelationEvaluator; | ||
22 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.RelationEvaluation; | ||
23 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
24 | import tools.refinery.interpreter.matchers.util.Clearable; | ||
25 | import tools.refinery.interpreter.matchers.util.Direction; | ||
26 | import tools.refinery.interpreter.matchers.util.timeline.Timeline; | ||
27 | import tools.refinery.interpreter.matchers.util.timeline.Timelines; | ||
28 | import tools.refinery.interpreter.rete.network.ProductionNode; | ||
29 | import tools.refinery.interpreter.rete.network.Receiver; | ||
30 | import tools.refinery.interpreter.rete.network.ReteContainer; | ||
31 | import tools.refinery.interpreter.rete.network.StandardNode; | ||
32 | import 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 | */ | ||
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 | } | ||