aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java')
-rw-r--r--subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java301
1 files changed, 301 insertions, 0 deletions
diff --git a/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java
new file mode 100644
index 00000000..e31d7b5c
--- /dev/null
+++ b/subprojects/viatra-runtime-localsearch/src/main/java/tools/refinery/viatra/runtime/localsearch/matcher/LocalSearchMatcher.java
@@ -0,0 +1,301 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Marton Bur, 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.localsearch.matcher;
10
11import java.util.ArrayList;
12import java.util.HashSet;
13import java.util.Iterator;
14import java.util.LinkedList;
15import java.util.List;
16import java.util.NoSuchElementException;
17import java.util.Objects;
18import java.util.Optional;
19import java.util.Set;
20import java.util.Spliterator;
21import java.util.Spliterators;
22import java.util.stream.Collectors;
23import java.util.stream.Stream;
24import java.util.stream.StreamSupport;
25
26import tools.refinery.viatra.runtime.localsearch.MatchingFrame;
27import tools.refinery.viatra.runtime.localsearch.plan.IPlanDescriptor;
28import tools.refinery.viatra.runtime.localsearch.plan.SearchPlan;
29import tools.refinery.viatra.runtime.localsearch.plan.SearchPlanExecutor;
30import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery;
31import tools.refinery.viatra.runtime.matchers.tuple.ITuple;
32import tools.refinery.viatra.runtime.matchers.tuple.Tuple;
33import tools.refinery.viatra.runtime.matchers.tuple.TupleMask;
34import tools.refinery.viatra.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
35import tools.refinery.viatra.runtime.matchers.util.Preconditions;
36
37/**
38 * @author Zoltan Ujhelyi
39 * @noinstantiate This class is not intended to be instantiated by clients.
40 */
41public final class LocalSearchMatcher implements ILocalSearchAdaptable {
42
43 private final List<SearchPlanExecutor> plan;
44 private final IPlanDescriptor planDescriptor;
45 private final List<ILocalSearchAdapter> adapters;
46
47 /**
48 * @since 2.0
49 */
50 public List<SearchPlanExecutor> getPlan() {
51 return plan;
52 }
53
54 @Override
55 public List<ILocalSearchAdapter> getAdapters() {
56 return new ArrayList<>(adapters);
57 }
58
59 private abstract class PlanExecutionIterator implements Iterator<Tuple> {
60
61 protected final Iterator<SearchPlanExecutor> planIterator;
62
63 protected SearchPlanExecutor currentPlan;
64 protected MatchingFrame frame;
65 protected final Set<ITuple> matchSet;
66 protected VolatileModifiableMaskedTuple parametersOfFrameView;
67 private boolean isNextMatchCalculated;
68
69 public PlanExecutionIterator(final Iterator<SearchPlanExecutor> planIterator) {
70 this.planIterator = planIterator;
71 isNextMatchCalculated = false;
72 matchSet = new HashSet<>();
73 }
74
75 protected boolean selectNextPlan() {
76 if(currentPlan != null) {
77 currentPlan.removeAdapters(adapters);
78 }
79 boolean validPlanSelected = false;
80
81 SearchPlanExecutor nextPlan = null;
82
83 while (!validPlanSelected && planIterator.hasNext()) {
84 nextPlan = planIterator.next();
85 nextPlan.addAdapters(adapters);
86 nextPlan.resetPlan();
87
88 validPlanSelected = initializeMatchingFrame(nextPlan);
89 }
90
91 if (validPlanSelected) {
92 for (ILocalSearchAdapter adapter : adapters) {
93 adapter.planChanged(Optional.ofNullable(currentPlan).map(SearchPlanExecutor::getSearchPlan),
94 Optional.ofNullable(nextPlan).map(SearchPlanExecutor::getSearchPlan));
95 }
96 currentPlan = nextPlan;
97 return true;
98 } else {
99 currentPlan = null;
100 return false;
101 }
102 }
103
104 protected abstract boolean initializeMatchingFrame(SearchPlanExecutor nextPlan);
105
106 private boolean findNextNewMatchInCurrentPlan() {
107 boolean foundMatch = currentPlan.execute(frame);
108 while (foundMatch && matchSet.contains(parametersOfFrameView)) {
109 for (ILocalSearchAdapter adapter : adapters) {
110 adapter.duplicateMatchFound(frame);
111 }
112 foundMatch = currentPlan.execute(frame);
113 }
114 return foundMatch;
115 }
116
117 @Override
118 public boolean hasNext() {
119 if (isNextMatchCalculated) {
120 return true;
121 }
122 if (currentPlan == null) {
123 return false;
124 }
125 boolean foundMatch = findNextNewMatchInCurrentPlan();
126
127 while (!foundMatch && planIterator.hasNext()) {
128 foundMatch = selectNextPlan() && findNextNewMatchInCurrentPlan();
129 }
130 if (!foundMatch) {
131 for (ILocalSearchAdapter adapter : adapters) {
132 adapter.noMoreMatchesAvailable(LocalSearchMatcher.this);
133 }
134 }
135 isNextMatchCalculated = foundMatch;
136 return foundMatch;
137 }
138
139 @Override
140 public Tuple next() {
141 if (!hasNext()) {
142 throw new NoSuchElementException("No more matches available.");
143 }
144 isNextMatchCalculated = false;
145 final Tuple match = parametersOfFrameView.toImmutable();
146 matchSet.add(match);
147 return match;
148 }
149 }
150
151 private class PlanExecutionIteratorWithArrayParameters extends PlanExecutionIterator {
152
153 private final Object[] parameterValues;
154
155 public PlanExecutionIteratorWithArrayParameters(Iterator<SearchPlanExecutor> planIterator, final Object[] parameterValues) {
156 super(planIterator);
157 this.parameterValues = parameterValues;
158 selectNextPlan();
159 }
160
161 protected boolean initializeMatchingFrame(SearchPlanExecutor nextPlan) {
162 frame = new MatchingFrame(nextPlan.getVariableMapping().size());
163 parametersOfFrameView = new VolatileModifiableMaskedTuple(frame, nextPlan.getParameterMask());
164 for (int i = 0; i < parameterValues.length; i++) {
165 Object valueToSet = parameterValues[i];
166 if (valueToSet != null) {
167 Object oldValue = parametersOfFrameView.get(i);
168 if (oldValue == null) {
169 parametersOfFrameView.set(i, valueToSet);
170 } else if (!Objects.equals(valueToSet, oldValue)) {
171 // Initial value setting resulted in contradictory values. This can happen because two parameter
172 // variables have been unified but the call provides different values for the parameters.
173 return false;
174 }
175 // If oldValue is not null but equal to newValue, the setting can be ignored
176 }
177 }
178
179 return true;
180 }
181 }
182 private class PlanExecutionIteratorWithTupleParameters extends PlanExecutionIterator {
183
184 private final ITuple parameterValues;
185 private final TupleMask parameterSeedMask;
186
187 public PlanExecutionIteratorWithTupleParameters(Iterator<SearchPlanExecutor> planIterator, final TupleMask parameterSeedMask, final ITuple parameterValues) {
188 super(planIterator);
189 this.parameterSeedMask = parameterSeedMask;
190 this.parameterValues = parameterValues;
191 selectNextPlan();
192 }
193
194 protected boolean initializeMatchingFrame(SearchPlanExecutor nextPlan) {
195 frame = new MatchingFrame(nextPlan.getVariableMapping().size());
196 parametersOfFrameView = new VolatileModifiableMaskedTuple(frame, nextPlan.getParameterMask());
197 for (int i = 0; i < parameterSeedMask.getSize(); i++) {
198 int index = parameterSeedMask.indices[i];
199 Object valueToSet = parameterValues.get(i);
200 if (valueToSet != null) {
201 Object oldValue = parametersOfFrameView.get(index);
202 if (oldValue == null) {
203 parametersOfFrameView.set(index, valueToSet);
204 } else if (!Objects.equals(valueToSet, oldValue)) {
205 // Initial value setting resulted in contradictory values. This can happen because two parameter
206 // variables have been unified but the call provides different values for the parameters.
207 return false;
208 }
209 // If oldValue is not null but equal to newValue, the setting can be ignored
210 }
211 }
212
213 return true;
214 }
215 }
216
217 /**
218 * @since 2.0
219 */
220 public LocalSearchMatcher(ISearchContext searchContext, IPlanDescriptor planDescriptor, List<SearchPlan> plan) {
221 Preconditions.checkArgument(planDescriptor != null, "Cannot initialize matcher with null query.");
222 this.planDescriptor = planDescriptor;
223 this.plan = plan.stream().map(p -> new SearchPlanExecutor(p, searchContext)).collect(Collectors.toList());
224 this.adapters = new LinkedList<>();
225 }
226
227 @Override
228 public void addAdapter(ILocalSearchAdapter adapter) {
229 this.adapters.add(adapter);
230 adapter.adapterRegistered(this);
231 }
232
233 @Override
234 public void removeAdapter(ILocalSearchAdapter adapter) {
235 this.adapters.remove(adapter);
236 adapter.adapterUnregistered(this);
237 }
238
239 @Override
240 public void addAdapters(List<ILocalSearchAdapter> adapters) {
241 this.adapters.addAll(adapters);
242 for (ILocalSearchAdapter adapter : adapters) {
243 adapter.adapterRegistered(this);
244 }
245 }
246
247 @Override
248 public void removeAdapters(List<ILocalSearchAdapter> adapters) {
249 this.adapters.removeAll(adapters);
250 for (ILocalSearchAdapter adapter : adapters) {
251 adapter.adapterUnregistered(this);
252 }
253 }
254
255 public int getParameterCount() {
256 return planDescriptor.getQuery().getParameters().size();
257 }
258
259 private void matchingStarted() {
260 for (ILocalSearchAdapter adapter : adapters) {
261 adapter.patternMatchingStarted(this);
262 }
263 }
264
265 /**
266 * @since 2.0
267 */
268 public Stream<Tuple> streamMatches(final Object[] parameterValues) {
269 matchingStarted();
270 PlanExecutionIterator it = new PlanExecutionIteratorWithArrayParameters(plan.iterator(), parameterValues);
271 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,
272 Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.DISTINCT), false);
273 }
274
275 /**
276 * @since 2.0
277 */
278 public Stream<Tuple> streamMatches(TupleMask parameterSeedMask, final ITuple parameterValues) {
279 matchingStarted();
280 PlanExecutionIterator it = new PlanExecutionIteratorWithTupleParameters(
281 plan.iterator(), parameterSeedMask, parameterValues);
282 return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,
283 Spliterator.IMMUTABLE | Spliterator.NONNULL | Spliterator.DISTINCT), false);
284 }
285
286 /**
287 * Returns the query specification this matcher used as source for the implementation
288 * @return never null
289 */
290 public PQuery getQuerySpecification() {
291 return planDescriptor.getQuery();
292 }
293
294
295 /**
296 * @since 1.5
297 */
298 public IPlanDescriptor getPlanDescriptor() {
299 return planDescriptor;
300 }
301}