diff options
author | Kristóf Marussy <kristof@marussy.com> | 2023-09-16 13:19:31 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2023-09-16 16:53:01 +0200 |
commit | 97b0c4c1192fe5580a7957c844acc8092b56c604 (patch) | |
tree | bea3cdf9aaeb5da2864fcf87780d356661af8f63 /subprojects/interpreter-localsearch/src/main/java/tools | |
parent | build: fix Sonar quality gate issues (diff) | |
download | refinery-97b0c4c1192fe5580a7957c844acc8092b56c604.tar.gz refinery-97b0c4c1192fe5580a7957c844acc8092b56c604.tar.zst refinery-97b0c4c1192fe5580a7957c844acc8092b56c604.zip |
chore: remove VIATRA branding
Rename VIATRA subprojects to Refinery Interpreter to avoid interfering with
Eclipse Foundation trademarks.
Uses refering to a specific (historical) version of VIATRA were kept to avoid
ambiguity.
Diffstat (limited to 'subprojects/interpreter-localsearch/src/main/java/tools')
74 files changed, 8368 insertions, 0 deletions
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/ExecutionLoggerAdapter.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/ExecutionLoggerAdapter.java new file mode 100644 index 00000000..bbfa2f4e --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/ExecutionLoggerAdapter.java | |||
@@ -0,0 +1,83 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.matcher.ILocalSearchAdapter; | ||
12 | import tools.refinery.interpreter.localsearch.matcher.LocalSearchMatcher; | ||
13 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
14 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
15 | import tools.refinery.interpreter.localsearch.plan.SearchPlan; | ||
16 | |||
17 | import java.util.Optional; | ||
18 | import java.util.function.Consumer; | ||
19 | |||
20 | /** | ||
21 | * @since 2.0 | ||
22 | */ | ||
23 | public final class ExecutionLoggerAdapter implements ILocalSearchAdapter { | ||
24 | |||
25 | volatile String indentation = ""; | ||
26 | private final Consumer<String> outputConsumer; | ||
27 | |||
28 | public ExecutionLoggerAdapter(Consumer<String> outputConsumer) { | ||
29 | this.outputConsumer = outputConsumer; | ||
30 | } | ||
31 | |||
32 | private void logMessage(String message) { | ||
33 | outputConsumer.accept(message); | ||
34 | } | ||
35 | |||
36 | private void logMessage(String message, Object...args) { | ||
37 | outputConsumer.accept(String.format(message, args)); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public void patternMatchingStarted(LocalSearchMatcher lsMatcher) { | ||
42 | logMessage(indentation + "[ START] " + lsMatcher.getQuerySpecification().getFullyQualifiedName()); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public void noMoreMatchesAvailable(LocalSearchMatcher lsMatcher) { | ||
47 | logMessage(indentation + "[FINISH] " + lsMatcher.getQuerySpecification().getFullyQualifiedName()); | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public void planChanged(Optional<SearchPlan> oldPlan, Optional<SearchPlan> newPlan) { | ||
52 | logMessage(indentation + "[ PLAN] " + newPlan.map(p -> p.getSourceBody().getPattern().getFullyQualifiedName()).orElse("")); | ||
53 | logMessage(indentation + newPlan.map(SearchPlan::toString).map(s -> s.replace("\n", "\n" + indentation)).orElse("")); | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public void operationSelected(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isBacktrack) { | ||
58 | String category = isBacktrack ? "[ BACK] " : "[SELECT] "; | ||
59 | logMessage(indentation + category + operation.toString()); | ||
60 | if (operation instanceof IPatternMatcherOperation) { | ||
61 | indentation = indentation + "\t"; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public void operationExecuted(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, | ||
67 | boolean isSuccessful) { | ||
68 | if (operation instanceof IPatternMatcherOperation && indentation.length() > 0) { | ||
69 | indentation = indentation.substring(1); | ||
70 | } | ||
71 | logMessage(indentation + "[ %s] %s %s", isSuccessful ? "OK" : "NO", operation.toString(), frame.toString()); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public void matchFound(SearchPlan plan, MatchingFrame frame) { | ||
76 | logMessage(indentation + "[ MATCH] " + plan.getSourceBody().getPattern().getFullyQualifiedName() + " " + frame.toString()); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public void duplicateMatchFound(MatchingFrame frame) { | ||
81 | logMessage(indentation + "[ DUPL.] " + frame.toString()); | ||
82 | } | ||
83 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/MatchingFrame.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/MatchingFrame.java new file mode 100644 index 00000000..6a597c19 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/MatchingFrame.java | |||
@@ -0,0 +1,114 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Akos Horvath, Gergely Varro Zoltan Ujhelyi 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 | |||
10 | package tools.refinery.interpreter.localsearch; | ||
11 | |||
12 | import tools.refinery.interpreter.matchers.tuple.IModifiableTuple; | ||
13 | import tools.refinery.interpreter.matchers.tuple.VolatileTuple; | ||
14 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
15 | |||
16 | import java.util.Arrays; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | /** | ||
20 | * A MatchingFrame is a Volatile Tuple implementation used by the local search engine internally. | ||
21 | */ | ||
22 | public class MatchingFrame extends VolatileTuple implements IModifiableTuple { | ||
23 | |||
24 | /** | ||
25 | * The array that physically holds the values. | ||
26 | */ | ||
27 | private Object[] frame; | ||
28 | |||
29 | /** | ||
30 | * @since 1.7 | ||
31 | */ | ||
32 | public MatchingFrame(int frameSize) { | ||
33 | this.frame = new Object[frameSize]; | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * Creates a copy of another matching frame; the two frames can be updated separately | ||
38 | * @param other | ||
39 | * @since 1.7 | ||
40 | */ | ||
41 | public MatchingFrame(MatchingFrame other) { | ||
42 | this.frame = Arrays.copyOf(other.frame, other.frame.length); | ||
43 | } | ||
44 | |||
45 | |||
46 | |||
47 | /** | ||
48 | * Returns the value stored inside the matching frame. | ||
49 | * | ||
50 | * @param position | ||
51 | * @return the element stored in the selected position in the frame, or null if it is not yet set | ||
52 | * @throws IndexOutOfBoundsException | ||
53 | * if position is negative | ||
54 | * @throws IllegalArgumentException | ||
55 | * if the position is larger then the length of the frame | ||
56 | */ | ||
57 | public Object getValue(int position) { | ||
58 | Preconditions.checkElementIndex(position, frame.length); | ||
59 | return frame[position]; | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Sets the value of the variable at the given position. For internal use in LS matching only. | ||
64 | * | ||
65 | * @param position the position of the variable within the frame | ||
66 | * @param value the value to be set for the variable | ||
67 | */ | ||
68 | public void setValue(int position, Object value) { | ||
69 | Preconditions.checkElementIndex(position, frame.length); | ||
70 | frame[position] = value; | ||
71 | } | ||
72 | |||
73 | public boolean testAndSetValue(Integer position, Object value) { | ||
74 | Preconditions.checkElementIndex(position, frame.length); | ||
75 | if (frame[position] == null) { | ||
76 | frame[position] = value; | ||
77 | return true; | ||
78 | } else { | ||
79 | return frame[position].equals(value); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public String toString() { | ||
85 | return Arrays.stream(frame).map(this::stringRepresentation).collect(Collectors.joining(", ", "[", "]")); | ||
86 | } | ||
87 | |||
88 | private String stringRepresentation(Object obj) { | ||
89 | if (obj == null) { | ||
90 | return "_"; | ||
91 | } | ||
92 | return obj.toString(); | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public int getSize() { | ||
97 | return frame.length; | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public Object get(int index) { | ||
102 | return getValue(index); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public Object[] getElements() { | ||
107 | return Arrays.copyOf(frame, frame.length); | ||
108 | } | ||
109 | |||
110 | @Override | ||
111 | public void set(int index, Object value) { | ||
112 | frame[index] = value; | ||
113 | } | ||
114 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/exceptions/LocalSearchException.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/exceptions/LocalSearchException.java new file mode 100644 index 00000000..46bffb1f --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/exceptions/LocalSearchException.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.interpreter.localsearch.exceptions; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
12 | |||
13 | /** | ||
14 | * @author Zoltan Ujhelyi, Akos Horvath | ||
15 | * | ||
16 | */ | ||
17 | public class LocalSearchException extends InterpreterRuntimeException { | ||
18 | |||
19 | private static final long serialVersionUID = -2585896573351435974L; | ||
20 | |||
21 | public static final String PLAN_EXECUTION_ERROR = "Error while executing search plan"; | ||
22 | public static final String TYPE_ERROR = "Invalid type of variable"; | ||
23 | |||
24 | public LocalSearchException(String description, Throwable rootException) { | ||
25 | super(description, rootException); | ||
26 | } | ||
27 | |||
28 | public LocalSearchException(String description) { | ||
29 | super(description); | ||
30 | } | ||
31 | |||
32 | |||
33 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/CallWithAdornment.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/CallWithAdornment.java new file mode 100644 index 00000000..e3906ccd --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/CallWithAdornment.java | |||
@@ -0,0 +1,55 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs 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.interpreter.localsearch.matcher; | ||
10 | |||
11 | import java.util.HashSet; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.interpreter.matchers.psystem.IQueryReference; | ||
15 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
17 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
18 | |||
19 | /** | ||
20 | * Immutable data that represents the role of a pattern call within an LS query plan. | ||
21 | * | ||
22 | * <p> The call is expressed as the {@link PConstraint} {@link #call} (implementing {@link IQueryReference}), | ||
23 | * while the stored {@link #adornment} records the way it will be used within a search plan (specifically, | ||
24 | * pattern parameters within the adornment will have their values known at the point of evaluating the constraint). | ||
25 | * | ||
26 | * | ||
27 | * @author Gabor Bergmann | ||
28 | * @since 2.1 | ||
29 | */ | ||
30 | public class CallWithAdornment { | ||
31 | private final IQueryReference call; | ||
32 | private final Set<PParameter> adornment; | ||
33 | |||
34 | public CallWithAdornment(IQueryReference call, Set<PParameter> adornment) { | ||
35 | this.call = call; | ||
36 | this.adornment = new HashSet<>(adornment); | ||
37 | } | ||
38 | |||
39 | public IQueryReference getCall() { | ||
40 | return call; | ||
41 | } | ||
42 | |||
43 | public Set<PParameter> getAdornment() { | ||
44 | return adornment; | ||
45 | } | ||
46 | |||
47 | |||
48 | public PQuery getReferredQuery() { | ||
49 | return call.getReferredQuery(); | ||
50 | } | ||
51 | |||
52 | public MatcherReference getMatcherReference() { | ||
53 | return new MatcherReference(getReferredQuery(), adornment); | ||
54 | } | ||
55 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ILocalSearchAdaptable.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ILocalSearchAdaptable.java new file mode 100644 index 00000000..9cec930f --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ILocalSearchAdaptable.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Peter Lunk, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.matcher; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | /** | ||
14 | * @author Zoltan Ujhelyi | ||
15 | * | ||
16 | */ | ||
17 | public interface ILocalSearchAdaptable { | ||
18 | |||
19 | List<ILocalSearchAdapter> getAdapters(); | ||
20 | |||
21 | void addAdapter(ILocalSearchAdapter adapter); | ||
22 | |||
23 | void removeAdapter(ILocalSearchAdapter adapter); | ||
24 | |||
25 | void removeAdapters(List<ILocalSearchAdapter> adapter); | ||
26 | |||
27 | void addAdapters(List<ILocalSearchAdapter> adapter); | ||
28 | |||
29 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ILocalSearchAdapter.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ILocalSearchAdapter.java new file mode 100644 index 00000000..088bbcba --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ILocalSearchAdapter.java | |||
@@ -0,0 +1,120 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.matcher; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | |||
13 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
14 | import tools.refinery.interpreter.localsearch.profiler.LocalSearchProfilerAdapter; | ||
15 | import tools.refinery.interpreter.localsearch.ExecutionLoggerAdapter; | ||
16 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
17 | import tools.refinery.interpreter.localsearch.plan.SearchPlan; | ||
18 | |||
19 | |||
20 | /** | ||
21 | * A local search adapter allows external code to follow the internal executions of the local search matcher. Possible | ||
22 | * implementations of the interface include profilers and debuggers. | ||
23 | * <p> | ||
24 | * <strong>EXPERIMENTAL</strong>. A few shortcomings have been found for this interface late during the development | ||
25 | * lifecycle of version 2.0 whose solution might need breaking possible future implementors. Because of this, right now | ||
26 | * it is not recommended to provide implementations outside of VIATRA. If necessary, have a look at the built-in | ||
27 | * adapters that should fulfill most cases in the meantime. See bugs https://bugs.eclipse.org/bugs/show_bug.cgi?id=535101 | ||
28 | * and https://bugs.eclipse.org/bugs/show_bug.cgi?id=535102 for details. | ||
29 | * | ||
30 | * @author Marton Bur | ||
31 | * @see ExecutionLoggerAdapter | ||
32 | * @see LocalSearchProfilerAdapter | ||
33 | * | ||
34 | */ | ||
35 | public interface ILocalSearchAdapter { | ||
36 | |||
37 | /** | ||
38 | * | ||
39 | * @since 1.2 | ||
40 | */ | ||
41 | default void adapterRegistered(ILocalSearchAdaptable adaptable) {}; | ||
42 | /** | ||
43 | * | ||
44 | * @since 1.2 | ||
45 | */ | ||
46 | default void adapterUnregistered(ILocalSearchAdaptable adaptable) {}; | ||
47 | |||
48 | /** | ||
49 | * Callback method to indicate the start of a matching process | ||
50 | * | ||
51 | * @param lsMatcher the local search matcher that starts the matching | ||
52 | */ | ||
53 | default void patternMatchingStarted(LocalSearchMatcher lsMatcher) {}; | ||
54 | |||
55 | /** | ||
56 | * Callback method to indicate the end of a matching process | ||
57 | * </p> | ||
58 | * <strong>WARNING</strong>: It is not guaranteed that this method will be called; | ||
59 | * it is possible that a match process will end after a match is found and no other matches are accessed. | ||
60 | * | ||
61 | * @param lsMatcher the local search matcher that finished | ||
62 | * @since 2.0 | ||
63 | */ | ||
64 | default void noMoreMatchesAvailable(LocalSearchMatcher lsMatcher) {}; | ||
65 | |||
66 | /** | ||
67 | * Callback method to indicate switching to a new plan during the execution of a pattern matching | ||
68 | * | ||
69 | * @param oldPlan the plan that is finished. Value is null when the first plan is starting. | ||
70 | * @param newPlan the plan that will begin execution | ||
71 | * @since 2.0 | ||
72 | */ | ||
73 | default void planChanged(Optional<SearchPlan> oldPlan, Optional<SearchPlan> newPlan) {}; | ||
74 | |||
75 | /** | ||
76 | * Callback method to indicate the selection of an operation to execute | ||
77 | * | ||
78 | * @param plan the current plan executor | ||
79 | * @param frame the current matching frame | ||
80 | * @param isBacktrack if true, the selected operation was reached via backtracking | ||
81 | * @since 2.0 | ||
82 | */ | ||
83 | default void operationSelected(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isBacktrack) {}; | ||
84 | |||
85 | /** | ||
86 | * Callback method to indicate that an operation is executed | ||
87 | * | ||
88 | * @param plan the current plan | ||
89 | * @param frame the current matching frame | ||
90 | * @param isSuccessful if true, the operation executed successfully, or false if the execution failed and backtracking will happen | ||
91 | * @since 2.0 | ||
92 | */ | ||
93 | default void operationExecuted(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isSuccessful) {}; | ||
94 | |||
95 | /** | ||
96 | * Callback that is used to indicate that a match has been found | ||
97 | * | ||
98 | * @param plan the search plan executor that found the match | ||
99 | * @param frame the frame that holds the substitutions of the variables that match | ||
100 | * @since 2.0 | ||
101 | */ | ||
102 | default void matchFound(SearchPlan plan, MatchingFrame frame) {}; | ||
103 | /** | ||
104 | * Callback that is used to indicate that the previously reported match has been found as a duplicate, thus will be ignored from the match results. | ||
105 | * | ||
106 | * @param plan the search plan executor that found the match | ||
107 | * @param frame the frame that holds the substitutions of the variables that match | ||
108 | * @since 2.0 | ||
109 | */ | ||
110 | default void duplicateMatchFound(MatchingFrame frame) {}; | ||
111 | |||
112 | /** | ||
113 | * Callback method to indicate that a search plan is initialized in an executor with the given frame and starting operation | ||
114 | * | ||
115 | * @param searchPlan | ||
116 | * @param frame | ||
117 | * @since 2.0 | ||
118 | */ | ||
119 | default void executorInitializing(SearchPlan searchPlan, MatchingFrame frame) {}; | ||
120 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ISearchContext.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ISearchContext.java new file mode 100644 index 00000000..1ac2fa53 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/ISearchContext.java | |||
@@ -0,0 +1,120 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, 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.interpreter.localsearch.matcher; | ||
10 | |||
11 | import org.apache.log4j.Logger; | ||
12 | import tools.refinery.interpreter.localsearch.matcher.integration.IAdornmentProvider; | ||
13 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
14 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
15 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
16 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
17 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
18 | import tools.refinery.interpreter.matchers.util.ICache; | ||
19 | import tools.refinery.interpreter.matchers.util.IProvider; | ||
20 | |||
21 | import java.util.Collections; | ||
22 | |||
23 | /** | ||
24 | * The {@link ISearchContext} interface allows search operations to reuse platform services such as the indexer. | ||
25 | * | ||
26 | * @author Zoltan Ujhelyi | ||
27 | * @noreference This interface is not intended to be referenced by clients. | ||
28 | * @noimplement This interface is not intended to be implemented by clients. | ||
29 | * @noextend This interface is not intended to be extended by clients. | ||
30 | * | ||
31 | */ | ||
32 | public interface ISearchContext { | ||
33 | |||
34 | /** | ||
35 | * Provides access to the generic query runtime context of the current engine | ||
36 | * @since 1.7 | ||
37 | */ | ||
38 | IQueryRuntimeContext getRuntimeContext(); | ||
39 | |||
40 | /** | ||
41 | * Returns a matcher for a selected query specification. | ||
42 | * | ||
43 | * @throws InterpreterRuntimeException | ||
44 | * @since 1.5 | ||
45 | */ | ||
46 | IQueryResultProvider getMatcher(CallWithAdornment dependency); | ||
47 | |||
48 | /** | ||
49 | * Allows search operations to cache values through the entire lifecycle of the local search backend. The values are | ||
50 | * calculated if not cached before using the given provider, or returned from the cache accordingly. | ||
51 | * | ||
52 | * @since 1.7 | ||
53 | */ | ||
54 | <T> T accessBackendLevelCache(Object key, Class<? extends T> clazz, IProvider<T> valueProvider); | ||
55 | |||
56 | /** | ||
57 | * Returns the engine-specific logger | ||
58 | * | ||
59 | * @since 2.0 | ||
60 | */ | ||
61 | Logger getLogger(); | ||
62 | |||
63 | /** | ||
64 | * @noreference This class is not intended to be referenced by clients. | ||
65 | * @noimplement This interface is not intended to be implemented by clients. | ||
66 | * @noextend This interface is not intended to be extended by clients. | ||
67 | */ | ||
68 | public class SearchContext implements ISearchContext { | ||
69 | |||
70 | private final IQueryRuntimeContext runtimeContext; | ||
71 | |||
72 | private final ICache backendLevelCache; | ||
73 | private final Logger logger; | ||
74 | private final ResultProviderRequestor resultProviderRequestor; | ||
75 | |||
76 | /** | ||
77 | * Initializes a search context using an arbitrary backend context | ||
78 | */ | ||
79 | public SearchContext(IQueryBackendContext backendContext, ICache backendLevelCache, | ||
80 | ResultProviderRequestor resultProviderRequestor) { | ||
81 | this.resultProviderRequestor = resultProviderRequestor; | ||
82 | this.runtimeContext = backendContext.getRuntimeContext(); | ||
83 | this.logger = backendContext.getLogger(); | ||
84 | |||
85 | this.backendLevelCache = backendLevelCache; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * @throws InterpreterRuntimeException | ||
90 | * @since 2.1 | ||
91 | */ | ||
92 | @Override | ||
93 | public IQueryResultProvider getMatcher(CallWithAdornment dependency) { | ||
94 | // Inject adornment for referenced pattern | ||
95 | IAdornmentProvider adornmentProvider = query -> { | ||
96 | if (query.equals(dependency.getReferredQuery())){ | ||
97 | return Collections.singleton(dependency.getAdornment()); | ||
98 | } | ||
99 | return Collections.emptySet(); | ||
100 | }; | ||
101 | return resultProviderRequestor.requestResultProvider(dependency.getCall(), | ||
102 | IAdornmentProvider.toHint(adornmentProvider)); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public <T> T accessBackendLevelCache(Object key, Class<? extends T> clazz, IProvider<T> valueProvider) { | ||
107 | return backendLevelCache.getValue(key, clazz, valueProvider); | ||
108 | } | ||
109 | |||
110 | public IQueryRuntimeContext getRuntimeContext() { | ||
111 | return runtimeContext; | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public Logger getLogger() { | ||
116 | return logger; | ||
117 | } | ||
118 | |||
119 | } | ||
120 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/LocalSearchMatcher.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/LocalSearchMatcher.java new file mode 100644 index 00000000..e26d08c7 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/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 | *******************************************************************************/ | ||
9 | package tools.refinery.interpreter.localsearch.matcher; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.LinkedList; | ||
15 | import java.util.List; | ||
16 | import java.util.NoSuchElementException; | ||
17 | import java.util.Objects; | ||
18 | import java.util.Optional; | ||
19 | import java.util.Set; | ||
20 | import java.util.Spliterator; | ||
21 | import java.util.Spliterators; | ||
22 | import java.util.stream.Collectors; | ||
23 | import java.util.stream.Stream; | ||
24 | import java.util.stream.StreamSupport; | ||
25 | |||
26 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
27 | import tools.refinery.interpreter.localsearch.plan.IPlanDescriptor; | ||
28 | import tools.refinery.interpreter.localsearch.plan.SearchPlan; | ||
29 | import tools.refinery.interpreter.localsearch.plan.SearchPlanExecutor; | ||
30 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
31 | import tools.refinery.interpreter.matchers.tuple.ITuple; | ||
32 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
33 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
34 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
35 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
36 | |||
37 | /** | ||
38 | * @author Zoltan Ujhelyi | ||
39 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
40 | */ | ||
41 | public 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 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/MatcherReference.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/MatcherReference.java new file mode 100644 index 00000000..8e1bec63 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/MatcherReference.java | |||
@@ -0,0 +1,97 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, 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 | *******************************************************************************/ | ||
9 | package tools.refinery.interpreter.localsearch.matcher; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint; | ||
14 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
15 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
16 | |||
17 | public class MatcherReference { | ||
18 | final PQuery query; | ||
19 | final Set<PParameter> adornment; | ||
20 | |||
21 | /** | ||
22 | * Hints that can override the callee's own hints. This field is intentionally left out from hashCode and equals | ||
23 | */ | ||
24 | final QueryEvaluationHint hints; | ||
25 | |||
26 | /** | ||
27 | * @since 1.4 | ||
28 | */ | ||
29 | public MatcherReference(PQuery query, Set<PParameter> adornment, QueryEvaluationHint hints) { | ||
30 | super(); | ||
31 | this.query = query; | ||
32 | this.adornment = adornment; | ||
33 | this.hints = hints; | ||
34 | } | ||
35 | |||
36 | public MatcherReference(PQuery query, Set<PParameter> adornment){ | ||
37 | this(query, adornment, null); | ||
38 | } | ||
39 | |||
40 | public PQuery getQuery() { | ||
41 | return query; | ||
42 | } | ||
43 | public Set<PParameter> getAdornment() { | ||
44 | return adornment; | ||
45 | } | ||
46 | @Override | ||
47 | public int hashCode() { | ||
48 | final int prime = 31; | ||
49 | int result = 1; | ||
50 | |||
51 | result = prime * result + ((adornment == null) ? 0 : adornment.hashCode()); | ||
52 | result = prime * result + ((query == null) ? 0 : query.hashCode()); | ||
53 | return result; | ||
54 | } | ||
55 | @Override | ||
56 | public boolean equals(Object obj) { | ||
57 | if (this == obj) | ||
58 | return true; | ||
59 | if (obj == null) | ||
60 | return false; | ||
61 | if (getClass() != obj.getClass()) | ||
62 | return false; | ||
63 | MatcherReference other = (MatcherReference) obj; | ||
64 | if (adornment == null) { | ||
65 | if (other.adornment != null) | ||
66 | return false; | ||
67 | } else if (!adornment.equals(other.adornment)) | ||
68 | return false; | ||
69 | if (query == null) { | ||
70 | if (other.query != null) | ||
71 | return false; | ||
72 | } else if (!query.equals(other.query)) | ||
73 | return false; | ||
74 | return true; | ||
75 | } | ||
76 | |||
77 | /** | ||
78 | * @return the hints to override the called reference's own hints with. Can be null. | ||
79 | * @since 1.4 | ||
80 | */ | ||
81 | public QueryEvaluationHint getHints() { | ||
82 | return hints; | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public String toString() { | ||
87 | StringBuilder sb = new StringBuilder(); | ||
88 | sb.append(query.getFullyQualifiedName()); | ||
89 | sb.append("("); | ||
90 | for(PParameter p : query.getParameters()){ | ||
91 | sb.append(adornment.contains(p) ? "b" : "f"); | ||
92 | } | ||
93 | sb.append(")"); | ||
94 | return sb.toString(); | ||
95 | } | ||
96 | |||
97 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/AbstractLocalSearchResultProvider.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/AbstractLocalSearchResultProvider.java new file mode 100644 index 00000000..44214f05 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/AbstractLocalSearchResultProvider.java | |||
@@ -0,0 +1,532 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.ArrayList; | ||
13 | import java.util.Collection; | ||
14 | import java.util.Collections; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.LinkedHashSet; | ||
18 | import java.util.LinkedList; | ||
19 | import java.util.List; | ||
20 | import java.util.Map; | ||
21 | import java.util.Objects; | ||
22 | import java.util.Optional; | ||
23 | import java.util.Queue; | ||
24 | import java.util.Set; | ||
25 | import java.util.concurrent.Callable; | ||
26 | import java.util.stream.Collectors; | ||
27 | import java.util.stream.IntStream; | ||
28 | import java.util.stream.Stream; | ||
29 | |||
30 | import tools.refinery.interpreter.localsearch.exceptions.LocalSearchException; | ||
31 | import tools.refinery.interpreter.localsearch.planner.compiler.IOperationCompiler; | ||
32 | import tools.refinery.interpreter.localsearch.matcher.CallWithAdornment; | ||
33 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
34 | import tools.refinery.interpreter.localsearch.matcher.LocalSearchMatcher; | ||
35 | import tools.refinery.interpreter.localsearch.matcher.MatcherReference; | ||
36 | import tools.refinery.interpreter.localsearch.plan.IPlanDescriptor; | ||
37 | import tools.refinery.interpreter.localsearch.plan.IPlanProvider; | ||
38 | import tools.refinery.interpreter.localsearch.plan.SearchPlan; | ||
39 | import tools.refinery.interpreter.localsearch.plan.SearchPlanForBody; | ||
40 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
41 | import tools.refinery.interpreter.matchers.backend.IMatcherCapability; | ||
42 | import tools.refinery.interpreter.matchers.backend.IQueryBackend; | ||
43 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
44 | import tools.refinery.interpreter.matchers.backend.IUpdateable; | ||
45 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint; | ||
46 | import tools.refinery.interpreter.matchers.backend.QueryHintOption; | ||
47 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
48 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
49 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
50 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
51 | import tools.refinery.interpreter.matchers.context.IndexingService; | ||
52 | import tools.refinery.interpreter.matchers.planning.QueryProcessingException; | ||
53 | import tools.refinery.interpreter.matchers.planning.helpers.FunctionalDependencyHelper; | ||
54 | import tools.refinery.interpreter.matchers.psystem.IQueryReference; | ||
55 | import tools.refinery.interpreter.matchers.psystem.PBody; | ||
56 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
57 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
58 | import tools.refinery.interpreter.matchers.psystem.queries.PQueries; | ||
59 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
60 | import tools.refinery.interpreter.matchers.psystem.rewriters.IFlattenCallPredicate; | ||
61 | import tools.refinery.interpreter.matchers.tuple.ITuple; | ||
62 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
63 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
64 | import tools.refinery.interpreter.matchers.util.Accuracy; | ||
65 | |||
66 | /** | ||
67 | * @author Zoltan Ujhelyi | ||
68 | * @since 1.7 | ||
69 | * | ||
70 | */ | ||
71 | public abstract class AbstractLocalSearchResultProvider implements IQueryResultProvider { | ||
72 | |||
73 | protected final LocalSearchBackend backend; | ||
74 | protected final IQueryBackendContext backendContext; | ||
75 | protected final IQueryRuntimeContext runtimeContext; | ||
76 | protected final PQuery query; | ||
77 | protected final QueryEvaluationHint userHints; | ||
78 | protected final Map<PQuery, LocalSearchHints> hintCache = new HashMap<>(); | ||
79 | protected final IPlanProvider planProvider; | ||
80 | private static final String PLAN_CACHE_KEY = AbstractLocalSearchResultProvider.class.getName() + "#planCache"; | ||
81 | private final Map<MatcherReference, IPlanDescriptor> planCache; | ||
82 | protected final ISearchContext searchContext; | ||
83 | /** | ||
84 | * @since 2.1 | ||
85 | */ | ||
86 | protected ResultProviderRequestor resultProviderRequestor; | ||
87 | |||
88 | /** | ||
89 | * @since 1.5 | ||
90 | */ | ||
91 | @SuppressWarnings({ "unchecked"}) | ||
92 | public AbstractLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query, | ||
93 | IPlanProvider planProvider, QueryEvaluationHint userHints) { | ||
94 | this.backend = backend; | ||
95 | this.backendContext = context; | ||
96 | this.query = query; | ||
97 | |||
98 | this.planProvider = planProvider; | ||
99 | this.userHints = userHints; | ||
100 | this.runtimeContext = context.getRuntimeContext(); | ||
101 | this.resultProviderRequestor = backend.getResultProviderRequestor(query, userHints); | ||
102 | this.searchContext = new ISearchContext.SearchContext(backendContext, backend.getCache(), resultProviderRequestor); | ||
103 | this.planCache = backend.getCache().getValue(PLAN_CACHE_KEY, Map.class, HashMap::new); | ||
104 | } | ||
105 | |||
106 | protected abstract IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext, LocalSearchHints configuration); | ||
107 | |||
108 | private IQueryRuntimeContext getRuntimeContext() { | ||
109 | return backend.getRuntimeContext(); | ||
110 | } | ||
111 | |||
112 | private LocalSearchMatcher createMatcher(IPlanDescriptor plan, final ISearchContext searchContext) { | ||
113 | List<SearchPlan> executors = plan.getPlan().stream() | ||
114 | .map(input -> new SearchPlan(input.getBody(), input.getCompiledOperations(), input.calculateParameterMask(), | ||
115 | input.getVariableKeys())) | ||
116 | .collect(Collectors.toList()); | ||
117 | return new LocalSearchMatcher(searchContext, plan, executors); | ||
118 | } | ||
119 | |||
120 | private IPlanDescriptor getOrCreatePlan(MatcherReference key, IQueryBackendContext backendContext, IOperationCompiler compiler, LocalSearchHints configuration, IPlanProvider planProvider) { | ||
121 | if (planCache.containsKey(key)){ | ||
122 | return planCache.get(key); | ||
123 | } else { | ||
124 | IPlanDescriptor plan = planProvider.getPlan(backendContext, compiler, | ||
125 | resultProviderRequestor, configuration, key); | ||
126 | planCache.put(key, plan); | ||
127 | return plan; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | private IPlanDescriptor getOrCreatePlan(MatcherReference key, IPlanProvider planProvider) { | ||
132 | if (planCache.containsKey(key)){ | ||
133 | return planCache.get(key); | ||
134 | } else { | ||
135 | LocalSearchHints configuration = overrideDefaultHints(key.getQuery()); | ||
136 | IOperationCompiler compiler = getOperationCompiler(backendContext, configuration); | ||
137 | IPlanDescriptor plan = planProvider.getPlan(backendContext, compiler, | ||
138 | resultProviderRequestor, configuration, key); | ||
139 | planCache.put(key, plan); | ||
140 | return plan; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | private LocalSearchHints overrideDefaultHints(PQuery pQuery) { | ||
145 | if (hintCache.containsKey(pQuery)) { | ||
146 | return hintCache.get(pQuery); | ||
147 | } else { | ||
148 | LocalSearchHints hint = LocalSearchHints.getDefaultOverriddenBy( | ||
149 | computeOverridingHints(pQuery)); | ||
150 | hintCache.put(pQuery, hint); | ||
151 | return hint; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * Combine with {@link QueryHintOption#getValueOrDefault(QueryEvaluationHint)} to access | ||
157 | * hint settings not covered by {@link LocalSearchHints} | ||
158 | */ | ||
159 | private QueryEvaluationHint computeOverridingHints(PQuery pQuery) { | ||
160 | return backendContext.getHintProvider().getQueryEvaluationHint(pQuery).overrideBy(userHints); | ||
161 | } | ||
162 | |||
163 | /** | ||
164 | * Prepare this result provider. This phase is separated from the constructor to allow the backend to cache its instance before | ||
165 | * requesting preparation for its dependencies. | ||
166 | * @since 1.5 | ||
167 | */ | ||
168 | public void prepare() { | ||
169 | try { | ||
170 | runtimeContext.coalesceTraversals(() -> { | ||
171 | LocalSearchHints configuration = overrideDefaultHints(query); | ||
172 | if (configuration.isUseBase()) { | ||
173 | indexInitializationBeforePlanning(); | ||
174 | } | ||
175 | prepareDirectDependencies(); | ||
176 | runtimeContext.executeAfterTraversal(AbstractLocalSearchResultProvider.this::preparePlansForExpectedAdornments); | ||
177 | return null; | ||
178 | }); | ||
179 | } catch (InvocationTargetException e) { | ||
180 | throw new QueryProcessingException("Error while building required indexes: {1}", new String[]{e.getTargetException().getMessage()}, "Error while building required indexes.", query, e); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | protected void preparePlansForExpectedAdornments() { | ||
185 | // Plan for possible adornments | ||
186 | for (Set<PParameter> adornment : overrideDefaultHints(query).getAdornmentProvider().getAdornments(query)) { | ||
187 | MatcherReference reference = new MatcherReference(query, adornment, userHints); | ||
188 | LocalSearchHints configuration = overrideDefaultHints(query); | ||
189 | IOperationCompiler compiler = getOperationCompiler(backendContext, configuration); | ||
190 | IPlanDescriptor plan = getOrCreatePlan(reference, backendContext, compiler, configuration, planProvider); | ||
191 | // Index keys | ||
192 | try { | ||
193 | if (configuration.isUseBase()) { | ||
194 | indexKeys(plan.getIteratedKeys()); | ||
195 | } | ||
196 | } catch (InvocationTargetException e) { | ||
197 | throw new QueryProcessingException(e.getMessage(), null, e.getMessage(), query, e); | ||
198 | } | ||
199 | //Prepare dependencies | ||
200 | for(SearchPlanForBody body: plan.getPlan()){ | ||
201 | for(CallWithAdornment dependency : body.getDependencies()){ | ||
202 | searchContext.getMatcher(dependency); | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | protected void prepareDirectDependencies() { | ||
209 | // Do not prepare for any adornment at this point | ||
210 | IAdornmentProvider adornmentProvider = input -> Collections.emptySet(); | ||
211 | QueryEvaluationHint adornmentHint = IAdornmentProvider.toHint(adornmentProvider); | ||
212 | |||
213 | for(IQueryReference call : getDirectDependencies()){ | ||
214 | resultProviderRequestor.requestResultProvider(call, adornmentHint); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | /** | ||
219 | * This method is called before planning start to allow indexing. It is important to note that this method is called | ||
220 | * inside a coalesceTraversals block, meaning (1) it is safe to add multiple registration requests as necessary, but | ||
221 | * (2) no value or statistics is available from the index. | ||
222 | * | ||
223 | * @throws InterpreterRuntimeException | ||
224 | */ | ||
225 | protected void indexInitializationBeforePlanning() { | ||
226 | // By default, no indexing is necessary | ||
227 | } | ||
228 | |||
229 | /** | ||
230 | * Collects and indexes all types _directly_ referred by the PQuery {@link #query}. Types indirect | ||
231 | * @param requiredIndexingServices | ||
232 | */ | ||
233 | protected void indexReferredTypesOfQuery(PQuery query, IndexingService requiredIndexingServices) { | ||
234 | PQueries.directlyRequiredTypesOfQuery(query, true /*only enumerables are considered for indexing */).forEach( | ||
235 | inputKey -> runtimeContext.ensureIndexed(inputKey, requiredIndexingServices) | ||
236 | ); | ||
237 | } | ||
238 | |||
239 | private Set<IQueryReference> getDirectDependencies() { | ||
240 | IFlattenCallPredicate flattenPredicate = overrideDefaultHints(query).getFlattenCallPredicate(); | ||
241 | Queue<PQuery> queue = new LinkedList<>(); | ||
242 | Set<PQuery> visited = new HashSet<>(); | ||
243 | Set<IQueryReference> result = new HashSet<>(); | ||
244 | queue.add(query); | ||
245 | |||
246 | while(!queue.isEmpty()){ | ||
247 | PQuery next = queue.poll(); | ||
248 | visited.add(next); | ||
249 | for(PBody body : next.getDisjunctBodies().getBodies()){ | ||
250 | for (IQueryReference call : body.getConstraintsOfType(IQueryReference.class)) { | ||
251 | if (call instanceof PositivePatternCall && | ||
252 | flattenPredicate.shouldFlatten((PositivePatternCall) call)) | ||
253 | { | ||
254 | PQuery dep = ((PositivePatternCall) call).getReferredQuery(); | ||
255 | if (!visited.contains(dep)){ | ||
256 | queue.add(dep); | ||
257 | } | ||
258 | } else { | ||
259 | result.add(call); | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | return result; | ||
265 | } | ||
266 | |||
267 | private LocalSearchMatcher initializeMatcher(Object[] parameters) { | ||
268 | return newLocalSearchMatcher(parameters); | ||
269 | } | ||
270 | |||
271 | private LocalSearchMatcher initializeMatcher(TupleMask parameterSeedMask) { | ||
272 | return newLocalSearchMatcher(parameterSeedMask.transformUnique(query.getParameters())); | ||
273 | |||
274 | } | ||
275 | |||
276 | |||
277 | /** | ||
278 | * @throws InterpreterRuntimeException | ||
279 | */ | ||
280 | public LocalSearchMatcher newLocalSearchMatcher(ITuple parameters) { | ||
281 | final Set<PParameter> adornment = new HashSet<>(); | ||
282 | for (int i = 0; i < parameters.getSize(); i++) { | ||
283 | if (parameters.get(i) != null) { | ||
284 | adornment.add(query.getParameters().get(i)); | ||
285 | } | ||
286 | } | ||
287 | |||
288 | return newLocalSearchMatcher(adornment); | ||
289 | } | ||
290 | |||
291 | /** | ||
292 | * @throws InterpreterRuntimeException | ||
293 | */ | ||
294 | public LocalSearchMatcher newLocalSearchMatcher(Object[] parameters) { | ||
295 | final Set<PParameter> adornment = new HashSet<>(); | ||
296 | for (int i = 0; i < parameters.length; i++) { | ||
297 | if (parameters[i] != null) { | ||
298 | adornment.add(query.getParameters().get(i)); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | return newLocalSearchMatcher(adornment); | ||
303 | } | ||
304 | |||
305 | private LocalSearchMatcher newLocalSearchMatcher(final Set<PParameter> adornment) { | ||
306 | final MatcherReference reference = new MatcherReference(query, adornment, userHints); | ||
307 | |||
308 | IPlanDescriptor plan = getOrCreatePlan(reference, planProvider); | ||
309 | if (overrideDefaultHints(reference.getQuery()).isUseBase()){ | ||
310 | try { | ||
311 | indexKeys(plan.getIteratedKeys()); | ||
312 | } catch (InvocationTargetException e) { | ||
313 | throw new LocalSearchException("Could not index keys", e); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | LocalSearchMatcher matcher = createMatcher(plan, searchContext); | ||
318 | matcher.addAdapters(backend.getAdapters()); | ||
319 | return matcher; | ||
320 | } | ||
321 | |||
322 | private void indexKeys(final Iterable<IInputKey> keys) throws InvocationTargetException { | ||
323 | final IQueryRuntimeContext qrc = getRuntimeContext(); | ||
324 | qrc.coalesceTraversals(new Callable<Void>() { | ||
325 | |||
326 | @Override | ||
327 | public Void call() throws Exception { | ||
328 | for(IInputKey key : keys){ | ||
329 | if (key.isEnumerable()) { | ||
330 | qrc.ensureIndexed(key, IndexingService.INSTANCES); | ||
331 | } | ||
332 | } | ||
333 | return null; | ||
334 | } | ||
335 | }); | ||
336 | } | ||
337 | |||
338 | @Override | ||
339 | public boolean hasMatch(Object[] parameters) { | ||
340 | final LocalSearchMatcher matcher = initializeMatcher(parameters); | ||
341 | return matcher.streamMatches(parameters).findAny().isPresent(); | ||
342 | } | ||
343 | |||
344 | @Override | ||
345 | public boolean hasMatch(TupleMask parameterSeedMask, ITuple parameters) { | ||
346 | final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask); | ||
347 | return matcher.streamMatches(parameterSeedMask, parameters).findAny().isPresent(); | ||
348 | } | ||
349 | |||
350 | @Override | ||
351 | public Optional<Tuple> getOneArbitraryMatch(Object[] parameters) { | ||
352 | final LocalSearchMatcher matcher = initializeMatcher(parameters); | ||
353 | return matcher.streamMatches(parameters).findAny(); | ||
354 | } | ||
355 | |||
356 | @Override | ||
357 | public Optional<Tuple> getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters) { | ||
358 | final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask); | ||
359 | return matcher.streamMatches(parameterSeedMask, parameters).findAny(); | ||
360 | } | ||
361 | |||
362 | @Override | ||
363 | public int countMatches(Object[] parameters) { | ||
364 | final LocalSearchMatcher matcher = initializeMatcher(parameters); | ||
365 | // Count returns long; casting to int - in case of integer overflow casting will throw the exception | ||
366 | return (int) matcher.streamMatches(parameters).count(); | ||
367 | } | ||
368 | |||
369 | @Override | ||
370 | public int countMatches(TupleMask parameterSeedMask, ITuple parameters) { | ||
371 | final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask); | ||
372 | // Count returns long; casting to int - in case of integer overflow casting will throw the exception | ||
373 | return (int) matcher.streamMatches(parameterSeedMask, parameters).count(); | ||
374 | } | ||
375 | |||
376 | private static final double ESTIMATE_CEILING = Long.MAX_VALUE / 16.0; | ||
377 | |||
378 | @Override | ||
379 | public Optional<Long> estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
380 | if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { // approximate using parameter types | ||
381 | final List<PParameter> parameters = query.getParameters(); | ||
382 | final Map<Set<Integer>, Set<Integer>> dependencies = backendContext.getQueryAnalyzer() | ||
383 | .getProjectedFunctionalDependencies(query, false); | ||
384 | |||
385 | List<Integer> projectionIndices = groupMask.getIndicesAsList(); | ||
386 | |||
387 | return estimateParameterCombinations(requiredAccuracy, parameters, dependencies, | ||
388 | projectionIndices, | ||
389 | Collections.emptySet() /* No parameters with fixed value */).map(Double::longValue); | ||
390 | } | ||
391 | else return Optional.empty(); | ||
392 | } | ||
393 | |||
394 | @Override | ||
395 | public Optional<Double> estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy) { | ||
396 | if (Accuracy.BEST_UPPER_BOUND.atLeastAsPreciseAs(requiredAccuracy)) { // approximate using parameter types | ||
397 | final List<PParameter> parameters = query.getParameters(); | ||
398 | final Map<Set<Integer>, Set<Integer>> dependencies = backendContext.getQueryAnalyzer() | ||
399 | .getProjectedFunctionalDependencies(query, false); | ||
400 | |||
401 | // all parameters used for the estimation - determinized order | ||
402 | final List<Integer> allParameterIndices = | ||
403 | IntStream.range(0, parameters.size()).boxed().collect(Collectors.toList()); | ||
404 | |||
405 | // some free parameters are functionally determined by bound parameters | ||
406 | final Set<Integer> boundOrImplied = FunctionalDependencyHelper.closureOf(groupMask.getIndicesAsList(), | ||
407 | dependencies); | ||
408 | |||
409 | return estimateParameterCombinations(requiredAccuracy, parameters, dependencies, | ||
410 | allParameterIndices, | ||
411 | boundOrImplied); | ||
412 | } | ||
413 | else return Optional.empty(); | ||
414 | } | ||
415 | |||
416 | /** | ||
417 | * @since 2.1 | ||
418 | * @noreference This method is not intended to be referenced by clients. | ||
419 | */ | ||
420 | public double estimateCost(TupleMask inputBindingMask) { | ||
421 | // TODO this is currently an abstract cost, not really a branching factor | ||
422 | |||
423 | HashSet<PParameter> adornment = new HashSet<>(inputBindingMask.transform(query.getParameters())); | ||
424 | final MatcherReference reference = new MatcherReference(query, adornment, userHints); | ||
425 | IPlanDescriptor plan = getOrCreatePlan(reference, planProvider); | ||
426 | |||
427 | return plan.getPlan().stream().mapToDouble(SearchPlanForBody::getCost).sum(); | ||
428 | } | ||
429 | |||
430 | /** | ||
431 | * Approximates using parameter types | ||
432 | */ | ||
433 | private Optional<Double> estimateParameterCombinations( | ||
434 | Accuracy requiredAccuracy, | ||
435 | final List<PParameter> parameters, | ||
436 | final Map<Set<Integer>, Set<Integer>> functionalDependencies, | ||
437 | final Collection<Integer> parameterIndicesToEstimate, | ||
438 | final Set<Integer> otherDeterminingIndices) | ||
439 | { | ||
440 | // keep order deterministic | ||
441 | LinkedHashSet<Integer> freeParameterIndices = new LinkedHashSet<>(parameterIndicesToEstimate); | ||
442 | |||
443 | // determining indices are bound | ||
444 | freeParameterIndices.removeAll(otherDeterminingIndices); | ||
445 | |||
446 | // some free parameters are functionally determined by other free parameters | ||
447 | for (Integer candidateForRemoval : new ArrayList<>(freeParameterIndices)) { | ||
448 | List<Integer> others = Stream.concat( | ||
449 | otherDeterminingIndices.stream(), | ||
450 | freeParameterIndices.stream().filter(index -> !Objects.equals(index, candidateForRemoval)) | ||
451 | ).collect(Collectors.toList()); | ||
452 | Set<Integer> othersClosure = FunctionalDependencyHelper.closureOf(others, functionalDependencies); | ||
453 | if (othersClosure.contains(candidateForRemoval)) { | ||
454 | // other parameters functionally determine this mone, does not count towards estimate | ||
455 | freeParameterIndices.remove(candidateForRemoval); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | |||
460 | Optional<Double> result = Optional.of(1.0); | ||
461 | // TODO this is currently works with declared types only. For better results, information from | ||
462 | // the Type inferrer should be included in the PSystem | ||
463 | for (int i = 0; (i < parameters.size()); i++) { | ||
464 | final IInputKey type = parameters.get(i).getDeclaredUnaryType(); | ||
465 | if (freeParameterIndices.contains(i) && type != null) { | ||
466 | result = result.flatMap(accumulator -> | ||
467 | runtimeContext.estimateCardinality(type, TupleMask.identity(1), requiredAccuracy).map(multiplier -> | ||
468 | Math.min(accumulator * multiplier, ESTIMATE_CEILING /* avoid overflow */) | ||
469 | )); | ||
470 | } | ||
471 | } | ||
472 | // TODO better approximate cardinality based on plan, branching factors, etc. | ||
473 | return result; | ||
474 | } | ||
475 | |||
476 | |||
477 | @Override | ||
478 | public Stream<Tuple> getAllMatches(Object[] parameters) { | ||
479 | final LocalSearchMatcher matcher = initializeMatcher(parameters); | ||
480 | return matcher.streamMatches(parameters); | ||
481 | } | ||
482 | |||
483 | @Override | ||
484 | public Stream<Tuple> getAllMatches(TupleMask parameterSeedMask, ITuple parameters) { | ||
485 | final LocalSearchMatcher matcher = initializeMatcher(parameterSeedMask); | ||
486 | return matcher.streamMatches(parameterSeedMask, parameters); | ||
487 | } | ||
488 | |||
489 | @Override | ||
490 | public IQueryBackend getQueryBackend() { | ||
491 | return backend; | ||
492 | } | ||
493 | |||
494 | @Override | ||
495 | public void addUpdateListener(IUpdateable listener, Object listenerTag, boolean fireNow) { | ||
496 | // throw new UnsupportedOperationException(UPDATE_LISTENER_NOT_SUPPORTED); | ||
497 | } | ||
498 | |||
499 | @Override | ||
500 | public void removeUpdateListener(Object listenerTag) { | ||
501 | // throw new UnsupportedOperationException(UPDATE_LISTENER_NOT_SUPPORTED); | ||
502 | } | ||
503 | |||
504 | /** | ||
505 | * @since 1.4 | ||
506 | */ | ||
507 | public IMatcherCapability getCapabilites() { | ||
508 | LocalSearchHints configuration = overrideDefaultHints(query); | ||
509 | return configuration; | ||
510 | } | ||
511 | |||
512 | /** | ||
513 | * Forgets all stored plans in this result provider. If no plans are stored, nothing happens. | ||
514 | * | ||
515 | * @since 2.0 | ||
516 | * @noreference This method is not intended to be referenced by clients; it should only used by {@link LocalSearchBackend}. | ||
517 | */ | ||
518 | public void forgetAllPlans() { | ||
519 | planCache.clear(); | ||
520 | } | ||
521 | |||
522 | /** | ||
523 | * Returns a search plan for a given adornment if exists | ||
524 | * | ||
525 | * @return a search plan for the pattern with the given adornment, or null if none exists | ||
526 | * @since 2.0 | ||
527 | * @noreference This method is not intended to be referenced by clients; it should only used by {@link LocalSearchBackend}. | ||
528 | */ | ||
529 | public IPlanDescriptor getSearchPlan(Set<PParameter> adornment) { | ||
530 | return planCache.get(new MatcherReference(query, adornment)); | ||
531 | } | ||
532 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/AllValidAdornments.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/AllValidAdornments.java new file mode 100644 index 00000000..146b53c5 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/AllValidAdornments.java | |||
@@ -0,0 +1,37 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import java.util.Set; | ||
12 | import java.util.stream.Collectors; | ||
13 | |||
14 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
15 | import tools.refinery.interpreter.matchers.psystem.queries.PParameterDirection; | ||
16 | import tools.refinery.interpreter.matchers.psystem.queries.PQueries; | ||
17 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
18 | import tools.refinery.interpreter.matchers.util.Sets; | ||
19 | |||
20 | |||
21 | /** | ||
22 | * This implementation calculates all valid adornments for the given query, respecting the parameter direction constraints. | ||
23 | * | ||
24 | * @author Grill Balázs | ||
25 | * @since 1.5 | ||
26 | */ | ||
27 | public class AllValidAdornments implements IAdornmentProvider { | ||
28 | |||
29 | @Override | ||
30 | public Iterable<Set<PParameter>> getAdornments(PQuery query) { | ||
31 | final Set<PParameter> ins = query.getParameters().stream().filter(PQueries.parameterDirectionPredicate(PParameterDirection.IN)).collect(Collectors.toSet()); | ||
32 | Set<PParameter> inouts = query.getParameters().stream().filter(PQueries.parameterDirectionPredicate(PParameterDirection.INOUT)).collect(Collectors.toSet()); | ||
33 | Set<? extends Set<PParameter>> possibleInouts = Sets.powerSet(inouts); | ||
34 | return possibleInouts.stream().map(input -> Sets.union(ins, input)).collect(Collectors.toSet()); | ||
35 | } | ||
36 | |||
37 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/DontFlattenDisjunctive.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/DontFlattenDisjunctive.java new file mode 100644 index 00000000..55fa53d2 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/DontFlattenDisjunctive.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
12 | import tools.refinery.interpreter.matchers.psystem.rewriters.IFlattenCallPredicate; | ||
13 | |||
14 | /** | ||
15 | * Forbids flattening of patterns that have more than one body. | ||
16 | * | ||
17 | * @since 2.1 | ||
18 | |||
19 | * @author Gabor Bergmann | ||
20 | * | ||
21 | */ | ||
22 | public class DontFlattenDisjunctive implements IFlattenCallPredicate { | ||
23 | |||
24 | @Override | ||
25 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
26 | return 1 >= positivePatternCall.getReferredQuery().getDisjunctBodies().getBodies().size(); | ||
27 | } | ||
28 | |||
29 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/DontFlattenIncrementalPredicate.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/DontFlattenIncrementalPredicate.java new file mode 100644 index 00000000..fd0e9456 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/DontFlattenIncrementalPredicate.java | |||
@@ -0,0 +1,44 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint; | ||
12 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint.BackendRequirement; | ||
13 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
14 | import tools.refinery.interpreter.matchers.psystem.rewriters.IFlattenCallPredicate; | ||
15 | |||
16 | /** | ||
17 | * This implementation forbids flattening of patterns marked to be executed with a caching / incremental backend. | ||
18 | * This makes is possible for the user to configure hybrid matching via using | ||
19 | * the 'search' and 'incremental keywords in the pattern definition file. | ||
20 | * | ||
21 | * @since 1.5 | ||
22 | * | ||
23 | */ | ||
24 | public class DontFlattenIncrementalPredicate implements IFlattenCallPredicate { | ||
25 | |||
26 | @Override | ||
27 | public boolean shouldFlatten(PositivePatternCall positivePatternCall) { | ||
28 | QueryEvaluationHint evaluationHints = positivePatternCall.getReferredQuery().getEvaluationHints(); | ||
29 | if (evaluationHints == null) return true; | ||
30 | |||
31 | BackendRequirement backendRequirementType = evaluationHints.getQueryBackendRequirementType(); | ||
32 | switch(backendRequirementType) { | ||
33 | case DEFAULT_CACHING: | ||
34 | return false; | ||
35 | case SPECIFIC: | ||
36 | return !evaluationHints.getQueryBackendFactory().isCaching(); | ||
37 | case UNSPECIFIED: | ||
38 | case DEFAULT_SEARCH: | ||
39 | default: | ||
40 | return true; | ||
41 | } | ||
42 | } | ||
43 | |||
44 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/GenericLocalSearchResultProvider.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/GenericLocalSearchResultProvider.java new file mode 100644 index 00000000..c4758053 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/GenericLocalSearchResultProvider.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.planner.compiler.GenericOperationCompiler; | ||
12 | import tools.refinery.interpreter.localsearch.planner.compiler.IOperationCompiler; | ||
13 | import tools.refinery.interpreter.localsearch.plan.IPlanProvider; | ||
14 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
15 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint; | ||
16 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
17 | import tools.refinery.interpreter.matchers.context.IndexingService; | ||
18 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
19 | |||
20 | /** | ||
21 | * @author Zoltan Ujhelyi | ||
22 | * @since 1.7 | ||
23 | * | ||
24 | */ | ||
25 | public class GenericLocalSearchResultProvider extends AbstractLocalSearchResultProvider { | ||
26 | |||
27 | /** | ||
28 | * @throws InterpreterRuntimeException | ||
29 | */ | ||
30 | public GenericLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query, | ||
31 | IPlanProvider planProvider, QueryEvaluationHint userHints) { | ||
32 | super(backend, context, query, planProvider, userHints); | ||
33 | } | ||
34 | |||
35 | @Override | ||
36 | protected void indexInitializationBeforePlanning() { | ||
37 | super.indexInitializationBeforePlanning(); | ||
38 | |||
39 | indexReferredTypesOfQuery(query, IndexingService.INSTANCES); | ||
40 | indexReferredTypesOfQuery(query, IndexingService.STATISTICS); | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | protected IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext, | ||
45 | LocalSearchHints configuration) { | ||
46 | return new GenericOperationCompiler(runtimeContext); | ||
47 | } | ||
48 | |||
49 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/IAdornmentProvider.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/IAdornmentProvider.java new file mode 100644 index 00000000..ebb65f57 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/IAdornmentProvider.java | |||
@@ -0,0 +1,72 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint; | ||
15 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint.BackendRequirement; | ||
16 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
17 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
18 | |||
19 | /** | ||
20 | * An adornment provider is used to define the adornments the pattern matcher should prepare for. | ||
21 | * | ||
22 | * <p>A default implementation is available in {@link AllValidAdornments} that describes all | ||
23 | * adornments fulfilling the parameter direction declarations; | ||
24 | * another default option (with better performance but restricted applicability) is {@link LazyPlanningAdornments}. | ||
25 | * | ||
26 | * <br><br> | ||
27 | * | ||
28 | * Users may implement this interface to limit the number of prepared plans based on some runtime information: | ||
29 | * | ||
30 | * <pre> | ||
31 | * class SomeAdornments{ | ||
32 | * | ||
33 | * public Iterable<Set<{@link PParameter}>> getAdornments({@link PQuery} query){ | ||
34 | * if (SomeGeneratedQuerySpecification.instance().getInternalQueryRepresentation().equals(query)){ | ||
35 | * return Collections.singleton(Sets.filter(Sets.newHashSet(query.getParameters()), new Predicate<PParameter>() { | ||
36 | * | ||
37 | * @Override | ||
38 | * public boolean apply(PParameter input) { | ||
39 | * // Decide whether this particular parameter will be bound | ||
40 | * return false; | ||
41 | * } | ||
42 | * })); | ||
43 | * } | ||
44 | * // Returning an empty iterable is safe for unknown queries | ||
45 | * return Collections.emptySet(); | ||
46 | * } | ||
47 | * | ||
48 | * } | ||
49 | * </pre> | ||
50 | * | ||
51 | * @author Grill Balázs | ||
52 | * @since 1.5 | ||
53 | * | ||
54 | */ | ||
55 | public interface IAdornmentProvider { | ||
56 | |||
57 | /** | ||
58 | * The bound parameter sets | ||
59 | */ | ||
60 | public Iterable<Set<PParameter>> getAdornments(PQuery query); | ||
61 | |||
62 | /** | ||
63 | * @return a simple hint that only overrides the adornment provider | ||
64 | * @since 2.1 | ||
65 | */ | ||
66 | public static QueryEvaluationHint toHint(IAdornmentProvider adornmentProvider) { | ||
67 | return new QueryEvaluationHint( | ||
68 | Collections.singletonMap(LocalSearchHintOptions.ADORNMENT_PROVIDER, adornmentProvider), | ||
69 | BackendRequirement.UNSPECIFIED); | ||
70 | } | ||
71 | |||
72 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LazyPlanningAdornments.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LazyPlanningAdornments.java new file mode 100644 index 00000000..db3bee60 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LazyPlanningAdornments.java | |||
@@ -0,0 +1,41 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
15 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
16 | |||
17 | /** | ||
18 | * This adornment provider does not trigger the preparation of any plans. | ||
19 | * Actual query plans will be computed on demand, when the first actual match request is made with a given adornment. | ||
20 | * | ||
21 | * <p> Caution: this is a safe default adornment provider for {@link GenericLocalSearchResultProvider} only; | ||
22 | * do not use for the EMF-specific LS backend. | ||
23 | * | ||
24 | * <p> The benefits is in execution time: query planning costs for adornments are postponed until first usage | ||
25 | * or even entirely avoided (when adornment is never used in practice). | ||
26 | * However, query evaluation time may become less predictable, as the first matcher call (with a given adornment) | ||
27 | * will include the planning cost. | ||
28 | * For benchmarking or other purposes where this is not desirable, use an adornment provider that demands plan precomputation for all necessary adornments. | ||
29 | * | ||
30 | * @author Gabor Bergmann | ||
31 | * @since 2.1 | ||
32 | * | ||
33 | */ | ||
34 | public class LazyPlanningAdornments implements IAdornmentProvider { | ||
35 | |||
36 | @Override | ||
37 | public Iterable<Set<PParameter>> getAdornments(PQuery query) { | ||
38 | return Collections.emptySet(); | ||
39 | } | ||
40 | |||
41 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchBackend.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchBackend.java new file mode 100644 index 00000000..de34ae00 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchBackend.java | |||
@@ -0,0 +1,259 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import java.lang.reflect.InvocationTargetException; | ||
12 | import java.util.ArrayList; | ||
13 | import java.util.Arrays; | ||
14 | import java.util.Collection; | ||
15 | import java.util.Collections; | ||
16 | import java.util.HashSet; | ||
17 | import java.util.List; | ||
18 | import java.util.Map; | ||
19 | import java.util.Set; | ||
20 | import java.util.stream.Stream; | ||
21 | |||
22 | import tools.refinery.interpreter.localsearch.exceptions.LocalSearchException; | ||
23 | import tools.refinery.interpreter.localsearch.matcher.ILocalSearchAdapter; | ||
24 | import tools.refinery.interpreter.localsearch.plan.IPlanDescriptor; | ||
25 | import tools.refinery.interpreter.localsearch.plan.IPlanProvider; | ||
26 | import tools.refinery.interpreter.localsearch.plan.SimplePlanProvider; | ||
27 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
28 | import tools.refinery.interpreter.matchers.backend.IMatcherCapability; | ||
29 | import tools.refinery.interpreter.matchers.backend.IQueryBackend; | ||
30 | import tools.refinery.interpreter.matchers.backend.IQueryBackendHintProvider; | ||
31 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
32 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint; | ||
33 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
34 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
35 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
36 | import tools.refinery.interpreter.matchers.psystem.analysis.QueryAnalyzer; | ||
37 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
38 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
39 | import tools.refinery.interpreter.matchers.util.CollectionsFactory; | ||
40 | import tools.refinery.interpreter.matchers.util.ICache; | ||
41 | import tools.refinery.interpreter.matchers.util.PurgableCache; | ||
42 | |||
43 | /** | ||
44 | * @author Marton Bur, Zoltan Ujhelyi | ||
45 | * @noextend This class is not intended to be subclassed by clients. | ||
46 | */ | ||
47 | public abstract class LocalSearchBackend implements IQueryBackend { | ||
48 | |||
49 | IQueryBackendContext context; | ||
50 | IPlanProvider planProvider; | ||
51 | private final Set<ILocalSearchAdapter> adapters = new HashSet<>(); | ||
52 | |||
53 | private final PurgableCache generalCache; | ||
54 | |||
55 | private final Map<PQuery, List<AbstractLocalSearchResultProvider>> resultProviderCache = CollectionsFactory.createMap(); | ||
56 | |||
57 | |||
58 | /** | ||
59 | * @since 1.5 | ||
60 | */ | ||
61 | public LocalSearchBackend(IQueryBackendContext context) { | ||
62 | super(); | ||
63 | this.context = context; | ||
64 | this.generalCache = new PurgableCache(); | ||
65 | this.planProvider = new SimplePlanProvider(context.getLogger()); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public void flushUpdates() { | ||
70 | |||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public IQueryResultProvider getResultProvider(PQuery query) { | ||
75 | return getResultProvider(query, null); | ||
76 | } | ||
77 | |||
78 | /** | ||
79 | * @since 1.4 | ||
80 | */ | ||
81 | @Override | ||
82 | public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints) { | ||
83 | |||
84 | final QueryEvaluationHint callHints = getHintProvider().getQueryEvaluationHint(query).overrideBy(hints); | ||
85 | IMatcherCapability requestedCapability = context.getRequiredMatcherCapability(query, callHints); | ||
86 | for(AbstractLocalSearchResultProvider existingResultProvider : resultProviderCache.getOrDefault(query, Collections.emptyList())){ | ||
87 | if (requestedCapability.canBeSubstitute(existingResultProvider.getCapabilites())){ | ||
88 | return existingResultProvider; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | AbstractLocalSearchResultProvider resultProvider = initializeResultProvider(query, hints); | ||
93 | resultProviderCache.computeIfAbsent(query, k->new ArrayList<>()).add(resultProvider); | ||
94 | resultProvider.prepare(); | ||
95 | return resultProvider; | ||
96 | } | ||
97 | |||
98 | /** | ||
99 | * Returns a requestor that this backend uses while processing pattern calls <i>from</i> this query. | ||
100 | * @noreference This method is not intended to be referenced by clients. | ||
101 | * @since 2.1 | ||
102 | */ | ||
103 | public ResultProviderRequestor getResultProviderRequestor(PQuery query, QueryEvaluationHint userHints) { | ||
104 | QueryEvaluationHint hintOnQuery = | ||
105 | context.getHintProvider().getQueryEvaluationHint(query).overrideBy(userHints); | ||
106 | LocalSearchHints defaultsApplied = LocalSearchHints.getDefaultOverriddenBy(hintOnQuery); | ||
107 | |||
108 | return new ResultProviderRequestor(this, | ||
109 | context.getResultProviderAccess(), | ||
110 | context.getHintProvider(), | ||
111 | defaultsApplied.getCallDelegationStrategy(), | ||
112 | userHints, | ||
113 | /* no global overrides */ null); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * @throws InterpreterRuntimeException | ||
118 | * @since 1.7 | ||
119 | */ | ||
120 | protected abstract AbstractLocalSearchResultProvider initializeResultProvider(PQuery query, QueryEvaluationHint hints); | ||
121 | |||
122 | @Override | ||
123 | public void dispose() { | ||
124 | resultProviderCache.clear(); | ||
125 | generalCache.purge(); | ||
126 | } | ||
127 | |||
128 | @Override | ||
129 | public boolean isCaching() { | ||
130 | return false; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * @since 2.0 | ||
135 | */ | ||
136 | @Override | ||
137 | public AbstractLocalSearchResultProvider peekExistingResultProvider(PQuery query) { | ||
138 | return resultProviderCache.getOrDefault(query, Collections.emptyList()).stream().findAny().orElse(null); | ||
139 | } | ||
140 | |||
141 | /** | ||
142 | * @since 1.4 | ||
143 | */ | ||
144 | public IQueryRuntimeContext getRuntimeContext() { | ||
145 | return context.getRuntimeContext(); | ||
146 | } | ||
147 | |||
148 | |||
149 | /** | ||
150 | * @since 1.5 | ||
151 | */ | ||
152 | public QueryAnalyzer getQueryAnalyzer() { | ||
153 | return context.getQueryAnalyzer(); | ||
154 | } | ||
155 | |||
156 | |||
157 | /** | ||
158 | * @since 1.4 | ||
159 | */ | ||
160 | public IQueryBackendHintProvider getHintProvider() { | ||
161 | return context.getHintProvider(); | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * @since 1.5 | ||
166 | */ | ||
167 | public void addAdapter(ILocalSearchAdapter adapter){ | ||
168 | adapters.add(adapter); | ||
169 | } | ||
170 | |||
171 | /** | ||
172 | * @since 1.5 | ||
173 | */ | ||
174 | public void removeAdapter(ILocalSearchAdapter adapter){ | ||
175 | adapters.remove(adapter); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * Return a copy of the current adapters | ||
180 | * @since 1.7 | ||
181 | */ | ||
182 | public List<ILocalSearchAdapter> getAdapters() { | ||
183 | return new ArrayList<>(adapters); | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @since 1.5 | ||
188 | */ | ||
189 | public IQueryBackendContext getBackendContext() { | ||
190 | return context; | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * Returns the internal cache of the backend | ||
195 | * @since 1.7 | ||
196 | * @noreference This method is not intended to be referenced by clients. | ||
197 | */ | ||
198 | public ICache getCache() { | ||
199 | return generalCache; | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * Updates the previously stored search plans for one or more given queries, computing a new set of plans if | ||
204 | * necessary. The new plans created are the same that would be created by executing prepare on the given query | ||
205 | * definitions. | ||
206 | * | ||
207 | * @since 2.0 | ||
208 | */ | ||
209 | public void recomputePlans(PQuery... queries) { | ||
210 | recomputePlans(Arrays.stream(queries).flatMap(query -> resultProviderCache.getOrDefault(query, Collections.emptyList()).stream())); | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * Updates the previously stored search plans for one or more given queries, computing a new set of plans if | ||
215 | * necessary The new plans created are the same that would be created by executing prepare on the given query | ||
216 | * definitions. | ||
217 | * | ||
218 | * @since 2.0 | ||
219 | */ | ||
220 | public void recomputePlans(Collection<PQuery> queries) { | ||
221 | recomputePlans(queries.stream().flatMap(query -> resultProviderCache.getOrDefault(query, Collections.emptyList()).stream())); | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * Updates the previously stored search plans for one or more given queries, computing a new set of plans if | ||
226 | * necessary The new plans created are the same that would be created by executing prepare on the given query | ||
227 | * definitions. | ||
228 | * | ||
229 | * @since 2.0 | ||
230 | */ | ||
231 | public void recomputePlans() { | ||
232 | recomputePlans(resultProviderCache.values().stream().flatMap(List::stream)); | ||
233 | } | ||
234 | |||
235 | private void recomputePlans(Stream<AbstractLocalSearchResultProvider> resultProviders) { | ||
236 | try { | ||
237 | context.getRuntimeContext().coalesceTraversals(() -> { | ||
238 | resultProviders.forEach(resultProvider -> { | ||
239 | resultProvider.forgetAllPlans(); | ||
240 | resultProvider.prepare(); | ||
241 | }); | ||
242 | return null; | ||
243 | }); | ||
244 | } catch (InvocationTargetException e) { | ||
245 | throw new LocalSearchException("Error while rebuilding plans: " + e.getMessage(), e); | ||
246 | } | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * Returns a search plan for a given query and adornment if such plan is already calculated. | ||
251 | * | ||
252 | * @return a previously calculated search plan for the given query and adornment, or null if no such plan exists | ||
253 | * @since 2.0 | ||
254 | */ | ||
255 | public IPlanDescriptor getSearchPlan(PQuery query, Set<PParameter> adornment) { | ||
256 | final AbstractLocalSearchResultProvider resultProvider = peekExistingResultProvider(query); | ||
257 | return (resultProvider == null) ? null : resultProvider.getSearchPlan(adornment); | ||
258 | } | ||
259 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchGenericBackendFactory.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchGenericBackendFactory.java new file mode 100644 index 00000000..4bdf0de4 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchGenericBackendFactory.java | |||
@@ -0,0 +1,65 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.backend.IMatcherCapability; | ||
12 | import tools.refinery.interpreter.matchers.backend.IQueryBackend; | ||
13 | import tools.refinery.interpreter.matchers.backend.IQueryBackendFactory; | ||
14 | import tools.refinery.interpreter.matchers.backend.QueryEvaluationHint; | ||
15 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
16 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * @author Marton Bur, Zoltan Ujhelyi | ||
20 | * @since 1.7 | ||
21 | * | ||
22 | */ | ||
23 | public enum LocalSearchGenericBackendFactory implements IQueryBackendFactory { | ||
24 | |||
25 | INSTANCE; | ||
26 | |||
27 | /** | ||
28 | * @since 1.5 | ||
29 | */ | ||
30 | @Override | ||
31 | public IQueryBackend create(IQueryBackendContext context) { | ||
32 | return new LocalSearchBackend(context) { | ||
33 | |||
34 | @Override | ||
35 | protected AbstractLocalSearchResultProvider initializeResultProvider(PQuery query, QueryEvaluationHint hints) { | ||
36 | return new GenericLocalSearchResultProvider(this, context, query, planProvider, hints); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public IQueryBackendFactory getFactory() { | ||
41 | return INSTANCE; | ||
42 | } | ||
43 | |||
44 | }; | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public Class<? extends IQueryBackend> getBackendClass() { | ||
49 | return LocalSearchBackend.class; | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * @since 1.4 | ||
54 | */ | ||
55 | @Override | ||
56 | public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) { | ||
57 | return LocalSearchHints.parse(hint); | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public boolean isCaching() { | ||
62 | return false; | ||
63 | } | ||
64 | |||
65 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchGenericBackendFactoryProvider.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchGenericBackendFactoryProvider.java new file mode 100644 index 00000000..cfa310f7 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchGenericBackendFactoryProvider.java | |||
@@ -0,0 +1,24 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.backend.IQueryBackendFactory; | ||
12 | import tools.refinery.interpreter.matchers.backend.IQueryBackendFactoryProvider; | ||
13 | |||
14 | /** | ||
15 | * @since 2.0 | ||
16 | */ | ||
17 | public class LocalSearchGenericBackendFactoryProvider implements IQueryBackendFactoryProvider { | ||
18 | |||
19 | @Override | ||
20 | public IQueryBackendFactory getFactory() { | ||
21 | return LocalSearchGenericBackendFactory.INSTANCE; | ||
22 | } | ||
23 | |||
24 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchHintOptions.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchHintOptions.java new file mode 100644 index 00000000..534d6c04 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchHintOptions.java | |||
@@ -0,0 +1,70 @@ | |||
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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.planner.cost.ICostFunction; | ||
12 | import tools.refinery.interpreter.localsearch.planner.cost.impl.IndexerBasedConstraintCostFunction; | ||
13 | import tools.refinery.interpreter.matchers.backend.ICallDelegationStrategy; | ||
14 | import tools.refinery.interpreter.matchers.backend.QueryHintOption; | ||
15 | import tools.refinery.interpreter.matchers.psystem.rewriters.IFlattenCallPredicate; | ||
16 | |||
17 | /** | ||
18 | * | ||
19 | * @author Gabor Bergmann | ||
20 | * @since 1.5 | ||
21 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
22 | */ | ||
23 | public final class LocalSearchHintOptions { | ||
24 | |||
25 | private LocalSearchHintOptions() { | ||
26 | // Private constructor for utility class | ||
27 | } | ||
28 | |||
29 | public static final QueryHintOption<Boolean> USE_BASE_INDEX = | ||
30 | hintOption("USE_BASE_INDEX", true); | ||
31 | |||
32 | // This key can be used to influence the core planner algorithm | ||
33 | public static final QueryHintOption<Integer> PLANNER_TABLE_ROW_COUNT = | ||
34 | hintOption("PLANNER_TABLE_ROW_COUNT", 4); | ||
35 | /** | ||
36 | * Cost function to be used by the planner. Must implement {@link ICostFunction} | ||
37 | * @since 1.4 | ||
38 | */ | ||
39 | public static final QueryHintOption<ICostFunction> PLANNER_COST_FUNCTION = | ||
40 | hintOption("PLANNER_COST_FUNCTION", new IndexerBasedConstraintCostFunction()); | ||
41 | /** | ||
42 | * Predicate to decide whether to flatten specific positive pattern calls {@link IFlattenCallPredicate} | ||
43 | * @since 1.4 | ||
44 | */ | ||
45 | public static final QueryHintOption<IFlattenCallPredicate> FLATTEN_CALL_PREDICATE = | ||
46 | hintOption("FLATTEN_CALL_PREDICATE", new DontFlattenDisjunctive()); | ||
47 | /** | ||
48 | * Strategy to decide how hints (most importantly, backend selection) propagate across pattern calls. | ||
49 | * Must implement {@link ICallDelegationStrategy}. | ||
50 | * @since 2.1 | ||
51 | */ | ||
52 | public static final QueryHintOption<ICallDelegationStrategy> CALL_DELEGATION_STRATEGY = | ||
53 | hintOption("CALL_DELEGATION_STRATEGY", ICallDelegationStrategy.FULL_BACKEND_ADHESION); | ||
54 | |||
55 | /** | ||
56 | * A provider of expected adornments {@link IAdornmentProvider}. | ||
57 | * | ||
58 | * The safe default is {@link AllValidAdornments}; | ||
59 | * however, the generic backend variant may safely use {@link LazyPlanningAdornments} instead. | ||
60 | * | ||
61 | * @since 1.5 | ||
62 | */ | ||
63 | public static final QueryHintOption<IAdornmentProvider> ADORNMENT_PROVIDER = | ||
64 | hintOption("ADORNMENT_PROVIDER", new AllValidAdornments()); | ||
65 | |||
66 | // internal helper for conciseness | ||
67 | private static <T, V extends T> QueryHintOption<T> hintOption(String hintKeyLocalName, V defaultValue) { | ||
68 | return new QueryHintOption<>(LocalSearchHintOptions.class, hintKeyLocalName, defaultValue); | ||
69 | } | ||
70 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchHints.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchHints.java new file mode 100644 index 00000000..5ccae128 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/matcher/integration/LocalSearchHints.java | |||
@@ -0,0 +1,306 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.matcher.integration; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.planner.cost.ICostFunction; | ||
12 | import tools.refinery.interpreter.localsearch.planner.cost.impl.IndexerBasedConstraintCostFunction; | ||
13 | import tools.refinery.interpreter.localsearch.planner.cost.impl.StatisticsBasedConstraintCostFunction; | ||
14 | import tools.refinery.interpreter.matchers.backend.*; | ||
15 | import tools.refinery.interpreter.matchers.psystem.rewriters.IFlattenCallPredicate; | ||
16 | import tools.refinery.interpreter.matchers.psystem.rewriters.IRewriterTraceCollector; | ||
17 | import tools.refinery.interpreter.matchers.psystem.rewriters.NopTraceCollector; | ||
18 | |||
19 | import java.util.HashMap; | ||
20 | import java.util.Map; | ||
21 | import java.util.Objects; | ||
22 | |||
23 | import static tools.refinery.interpreter.localsearch.matcher.integration.LocalSearchHintOptions.*; | ||
24 | import static tools.refinery.interpreter.matchers.backend.CommonQueryHintOptions.normalizationTraceCollector; | ||
25 | |||
26 | /** | ||
27 | * Type safe builder and extractor for Local search specific hints | ||
28 | * | ||
29 | * @author Grill Balázs | ||
30 | * @since 1.4 | ||
31 | * | ||
32 | */ | ||
33 | public final class LocalSearchHints implements IMatcherCapability { | ||
34 | |||
35 | private Boolean useBase = null; | ||
36 | |||
37 | private Integer rowCount = null; | ||
38 | |||
39 | private ICostFunction costFunction = null; | ||
40 | |||
41 | private IFlattenCallPredicate flattenCallPredicate = null; | ||
42 | |||
43 | private ICallDelegationStrategy callDelegationStrategy = null; | ||
44 | |||
45 | private IAdornmentProvider adornmentProvider = null; | ||
46 | |||
47 | private IRewriterTraceCollector traceCollector = NopTraceCollector.INSTANCE; | ||
48 | |||
49 | private IQueryBackendFactory backendFactory = null; | ||
50 | |||
51 | private LocalSearchHints() {} | ||
52 | |||
53 | /** | ||
54 | * Return the default settings overridden by the given hints | ||
55 | */ | ||
56 | public static LocalSearchHints getDefaultOverriddenBy(QueryEvaluationHint overridingHint){ | ||
57 | return parse(getDefault().build(overridingHint)); | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * Default settings which are considered the most safe, providing a reasonable performance for most of the cases. Assumes the availability of the base indexer. | ||
62 | */ | ||
63 | public static LocalSearchHints getDefault(){ | ||
64 | return getDefaultGeneric(); | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * Initializes the generic (not EMF specific) search backend with the default settings | ||
69 | * @since 1.7 | ||
70 | */ | ||
71 | public static LocalSearchHints getDefaultGeneric(){ | ||
72 | LocalSearchHints result = new LocalSearchHints(); | ||
73 | result.useBase = true; // Should be unused; but a false value might cause surprises as an engine-default hint | ||
74 | result.rowCount = 4; | ||
75 | result.costFunction = new IndexerBasedConstraintCostFunction(StatisticsBasedConstraintCostFunction.INVERSE_NAVIGATION_PENALTY_GENERIC); | ||
76 | result.flattenCallPredicate = FLATTEN_CALL_PREDICATE.getDefaultValue(); | ||
77 | result.callDelegationStrategy = ICallDelegationStrategy.FULL_BACKEND_ADHESION; | ||
78 | result.adornmentProvider = new LazyPlanningAdornments(); | ||
79 | result.backendFactory = LocalSearchGenericBackendFactory.INSTANCE; | ||
80 | return result; | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Initializes the default search backend with hybrid-enabled settings | ||
85 | * @since 2.1 | ||
86 | */ | ||
87 | public static LocalSearchHints getDefaultHybrid(){ | ||
88 | LocalSearchHints result = getDefault(); | ||
89 | result.callDelegationStrategy = ICallDelegationStrategy.PARTIAL_BACKEND_ADHESION; | ||
90 | result.flattenCallPredicate = new IFlattenCallPredicate.And( | ||
91 | new DontFlattenIncrementalPredicate(), new DontFlattenDisjunctive()); | ||
92 | return result; | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Initializes the generic (not EMF specific) search backend with hybrid-enabled settings | ||
97 | * @since 2.1 | ||
98 | */ | ||
99 | public static LocalSearchHints getDefaultGenericHybrid(){ | ||
100 | LocalSearchHints result = getDefaultGeneric(); | ||
101 | result.callDelegationStrategy = ICallDelegationStrategy.PARTIAL_BACKEND_ADHESION; | ||
102 | result.flattenCallPredicate = new IFlattenCallPredicate.And( | ||
103 | new DontFlattenIncrementalPredicate(), new DontFlattenDisjunctive()); | ||
104 | return result; | ||
105 | } | ||
106 | |||
107 | public static LocalSearchHints parse(QueryEvaluationHint hint){ | ||
108 | LocalSearchHints result = new LocalSearchHints(); | ||
109 | |||
110 | result.useBase = USE_BASE_INDEX.getValueOrNull(hint); | ||
111 | result.rowCount = PLANNER_TABLE_ROW_COUNT.getValueOrNull(hint); | ||
112 | result.flattenCallPredicate = FLATTEN_CALL_PREDICATE.getValueOrNull(hint); | ||
113 | result.callDelegationStrategy = CALL_DELEGATION_STRATEGY.getValueOrNull(hint); | ||
114 | result.costFunction = PLANNER_COST_FUNCTION.getValueOrNull(hint); | ||
115 | result.adornmentProvider = ADORNMENT_PROVIDER.getValueOrNull(hint); | ||
116 | result.traceCollector = normalizationTraceCollector.getValueOrDefault(hint); | ||
117 | |||
118 | return result; | ||
119 | } | ||
120 | |||
121 | |||
122 | private Map<QueryHintOption<?>, Object> calculateHintMap() { | ||
123 | Map<QueryHintOption<?>, Object> map = new HashMap<>(); | ||
124 | if (useBase != null){ | ||
125 | USE_BASE_INDEX.insertOverridingValue(map, useBase); | ||
126 | } | ||
127 | if (rowCount != null){ | ||
128 | PLANNER_TABLE_ROW_COUNT.insertOverridingValue(map, rowCount); | ||
129 | } | ||
130 | if (costFunction != null){ | ||
131 | PLANNER_COST_FUNCTION.insertOverridingValue(map, costFunction); | ||
132 | } | ||
133 | if (flattenCallPredicate != null){ | ||
134 | FLATTEN_CALL_PREDICATE.insertOverridingValue(map, flattenCallPredicate); | ||
135 | } | ||
136 | if (callDelegationStrategy != null){ | ||
137 | CALL_DELEGATION_STRATEGY.insertOverridingValue(map, callDelegationStrategy); | ||
138 | } | ||
139 | if (adornmentProvider != null){ | ||
140 | ADORNMENT_PROVIDER.insertOverridingValue(map, adornmentProvider); | ||
141 | } | ||
142 | if (traceCollector != null){ | ||
143 | normalizationTraceCollector.insertOverridingValue(map, traceCollector); | ||
144 | } | ||
145 | return map; | ||
146 | } | ||
147 | |||
148 | public QueryEvaluationHint build(){ | ||
149 | Map<QueryHintOption<?>, Object> map = calculateHintMap(); | ||
150 | return new QueryEvaluationHint(map, backendFactory); | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * @since 1.7 | ||
155 | */ | ||
156 | public QueryEvaluationHint build(QueryEvaluationHint overridingHint) { | ||
157 | if (overridingHint == null) | ||
158 | return build(); | ||
159 | |||
160 | IQueryBackendFactory factory = (overridingHint.getQueryBackendFactory() == null) | ||
161 | ? this.backendFactory | ||
162 | : overridingHint.getQueryBackendFactory(); | ||
163 | |||
164 | Map<QueryHintOption<?>, Object> hints = calculateHintMap(); | ||
165 | if (overridingHint.getBackendHintSettings() != null) { | ||
166 | hints.putAll(overridingHint.getBackendHintSettings()); | ||
167 | } | ||
168 | |||
169 | return new QueryEvaluationHint(hints, factory); | ||
170 | } | ||
171 | |||
172 | public boolean isUseBase() { | ||
173 | return useBase; | ||
174 | } | ||
175 | |||
176 | public ICostFunction getCostFunction() { | ||
177 | return costFunction; | ||
178 | } | ||
179 | |||
180 | public IFlattenCallPredicate getFlattenCallPredicate() { | ||
181 | return flattenCallPredicate; | ||
182 | } | ||
183 | |||
184 | /** | ||
185 | * @since 2.1 | ||
186 | */ | ||
187 | public ICallDelegationStrategy getCallDelegationStrategy() { | ||
188 | return callDelegationStrategy; | ||
189 | } | ||
190 | |||
191 | public Integer getRowCount() { | ||
192 | return rowCount; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * @since 1.5 | ||
197 | */ | ||
198 | public IAdornmentProvider getAdornmentProvider() { | ||
199 | return adornmentProvider; | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * @since 1.6 | ||
204 | */ | ||
205 | public IRewriterTraceCollector getTraceCollector() { | ||
206 | return traceCollector == null ? normalizationTraceCollector.getDefaultValue() : traceCollector; | ||
207 | } | ||
208 | |||
209 | public LocalSearchHints setUseBase(boolean useBase) { | ||
210 | this.useBase = useBase; | ||
211 | return this; | ||
212 | } | ||
213 | |||
214 | public LocalSearchHints setRowCount(int rowCount) { | ||
215 | this.rowCount = rowCount; | ||
216 | return this; | ||
217 | } | ||
218 | |||
219 | public LocalSearchHints setCostFunction(ICostFunction costFunction) { | ||
220 | this.costFunction = costFunction; | ||
221 | return this; | ||
222 | } | ||
223 | |||
224 | public LocalSearchHints setFlattenCallPredicate(IFlattenCallPredicate flattenCallPredicate) { | ||
225 | this.flattenCallPredicate = flattenCallPredicate; | ||
226 | return this; | ||
227 | } | ||
228 | |||
229 | |||
230 | /** | ||
231 | * @since 2.1 | ||
232 | */ | ||
233 | public LocalSearchHints setCallDelegationStrategy(ICallDelegationStrategy callDelegationStrategy) { | ||
234 | this.callDelegationStrategy = callDelegationStrategy; | ||
235 | return this; | ||
236 | } | ||
237 | |||
238 | /** | ||
239 | * @since 1.6 | ||
240 | */ | ||
241 | public LocalSearchHints setTraceCollector(IRewriterTraceCollector traceCollector) { | ||
242 | this.traceCollector = traceCollector; | ||
243 | return this; | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * @since 1.5 | ||
248 | */ | ||
249 | public LocalSearchHints setAdornmentProvider(IAdornmentProvider adornmentProvider) { | ||
250 | this.adornmentProvider = adornmentProvider; | ||
251 | return this; | ||
252 | } | ||
253 | |||
254 | public static LocalSearchHints customizeUseBase(boolean useBase){ | ||
255 | return new LocalSearchHints().setUseBase(useBase); | ||
256 | } | ||
257 | |||
258 | public static LocalSearchHints customizeRowCount(int rowCount){ | ||
259 | return new LocalSearchHints().setRowCount(rowCount); | ||
260 | } | ||
261 | |||
262 | public static LocalSearchHints customizeCostFunction(ICostFunction costFunction){ | ||
263 | return new LocalSearchHints().setCostFunction(costFunction); | ||
264 | } | ||
265 | |||
266 | public static LocalSearchHints customizeFlattenCallPredicate(IFlattenCallPredicate predicate){ | ||
267 | return new LocalSearchHints().setFlattenCallPredicate(predicate); | ||
268 | } | ||
269 | |||
270 | /** | ||
271 | * @since 2.1 | ||
272 | */ | ||
273 | public static LocalSearchHints customizeCallDelegationStrategy(ICallDelegationStrategy strategy){ | ||
274 | return new LocalSearchHints().setCallDelegationStrategy(strategy); | ||
275 | } | ||
276 | |||
277 | /** | ||
278 | * @since 1.5 | ||
279 | */ | ||
280 | public static LocalSearchHints customizeAdornmentProvider(IAdornmentProvider adornmentProvider){ | ||
281 | return new LocalSearchHints().setAdornmentProvider(adornmentProvider); | ||
282 | } | ||
283 | |||
284 | /** | ||
285 | * @since 1.6 | ||
286 | */ | ||
287 | public static LocalSearchHints customizeTraceCollector(IRewriterTraceCollector traceCollector){ | ||
288 | return new LocalSearchHints().setTraceCollector(traceCollector); | ||
289 | } | ||
290 | |||
291 | @Override | ||
292 | public boolean canBeSubstitute(IMatcherCapability capability) { | ||
293 | if (capability instanceof LocalSearchHints){ | ||
294 | LocalSearchHints other = (LocalSearchHints)capability; | ||
295 | /* | ||
296 | * We allow substitution of matchers if their functionally relevant settings are equal. | ||
297 | */ | ||
298 | return Objects.equals(other.useBase, useBase); | ||
299 | } | ||
300 | /* | ||
301 | * For any other cases (e.g. for Rete), we cannot assume | ||
302 | * that matchers created by LS are functionally equivalent. | ||
303 | */ | ||
304 | return false; | ||
305 | } | ||
306 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/CheckOperationExecutor.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/CheckOperationExecutor.java new file mode 100644 index 00000000..cd66efbd --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/CheckOperationExecutor.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
12 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
13 | |||
14 | /** | ||
15 | * Abstract base class for search operations that check only the already set variables. | ||
16 | * | ||
17 | * @noextend This class is not intended to be subclassed by clients. | ||
18 | * @since 2.0 | ||
19 | */ | ||
20 | public abstract class CheckOperationExecutor implements ISearchOperation.ISearchOperationExecutor { | ||
21 | |||
22 | /** | ||
23 | * The executed field ensures that the second call of the check always returns false, resulting in a quick | ||
24 | * backtracking. | ||
25 | */ | ||
26 | private boolean executed; | ||
27 | |||
28 | @Override | ||
29 | public void onInitialize(MatchingFrame frame, ISearchContext context) { | ||
30 | executed = false; | ||
31 | } | ||
32 | |||
33 | @Override | ||
34 | public void onBacktrack(MatchingFrame frame, ISearchContext context) { | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public boolean execute(MatchingFrame frame, ISearchContext context) { | ||
39 | executed = executed ? false : check(frame, context); | ||
40 | return executed; | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Executes the checking operation | ||
45 | * @since 1.7 | ||
46 | */ | ||
47 | protected abstract boolean check(MatchingFrame frame, ISearchContext context) ; | ||
48 | |||
49 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/ExtendOperationExecutor.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/ExtendOperationExecutor.java new file mode 100644 index 00000000..ecf2952c --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/ExtendOperationExecutor.java | |||
@@ -0,0 +1,76 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | package tools.refinery.interpreter.localsearch.operations; | ||
11 | |||
12 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
13 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
14 | |||
15 | import java.util.Iterator; | ||
16 | |||
17 | /** | ||
18 | * An operation that can be used to enumerate all possible values for a single position based on a constraint | ||
19 | * @author Zoltan Ujhelyi, Akos Horvath | ||
20 | * @noextend This class is not intended to be subclassed by clients. | ||
21 | * @since 2.0 | ||
22 | */ | ||
23 | public abstract class ExtendOperationExecutor<T> implements ISearchOperation.ISearchOperationExecutor { | ||
24 | |||
25 | private Iterator<? extends T> it; | ||
26 | |||
27 | /** | ||
28 | * Returns an iterator with the possible options from the current state | ||
29 | * @since 2.0 | ||
30 | */ | ||
31 | protected abstract Iterator<? extends T> getIterator(MatchingFrame frame, ISearchContext context); | ||
32 | /** | ||
33 | * Updates the frame with the next element of the iterator. Called during {@link #execute(MatchingFrame, ISearchContext)}. | ||
34 | * | ||
35 | * @return true if the update is successful or false otherwise; in case of false is returned, the next element should be taken from the iterator. | ||
36 | * @since 2.0 | ||
37 | */ | ||
38 | protected abstract boolean fillInValue(T newValue, MatchingFrame frame, ISearchContext context); | ||
39 | |||
40 | /** | ||
41 | * Restores the frame to the state before {@link #fillInValue(Object, MatchingFrame, ISearchContext)}. Called during | ||
42 | * {@link #onBacktrack(MatchingFrame, ISearchContext)}. | ||
43 | * | ||
44 | * @since 2.0 | ||
45 | */ | ||
46 | protected abstract void cleanup(MatchingFrame frame, ISearchContext context); | ||
47 | |||
48 | @Override | ||
49 | public void onInitialize(MatchingFrame frame, ISearchContext context) { | ||
50 | it = getIterator(frame, context); | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public void onBacktrack(MatchingFrame frame, ISearchContext context) { | ||
55 | it = null; | ||
56 | |||
57 | } | ||
58 | |||
59 | /** | ||
60 | * Fixed version that handles failed unification of variables correctly. | ||
61 | * @param frame The matching frame to extend. | ||
62 | * @param context The search context. | ||
63 | * @return {@code true} if an extension was found, {@code false} otherwise. | ||
64 | */ | ||
65 | @Override | ||
66 | public boolean execute(MatchingFrame frame, ISearchContext context) { | ||
67 | while (it.hasNext()) { | ||
68 | var newValue = it.next(); | ||
69 | if (fillInValue(newValue, frame, context)) { | ||
70 | return true; | ||
71 | } | ||
72 | } | ||
73 | return false; | ||
74 | } | ||
75 | |||
76 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/IIteratingSearchOperation.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/IIteratingSearchOperation.java new file mode 100644 index 00000000..85f0b6ac --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/IIteratingSearchOperation.java | |||
@@ -0,0 +1,27 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.localsearch.operations; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
12 | |||
13 | /** | ||
14 | * Denotes a {@link ISearchOperation} which involves iterating over an instances of an {@link IInputKey} | ||
15 | * | ||
16 | * @author Grill Balázs | ||
17 | * @since 1.4 | ||
18 | * | ||
19 | */ | ||
20 | public interface IIteratingSearchOperation extends ISearchOperation{ | ||
21 | |||
22 | /** | ||
23 | * Get the {@link IInputKey} which instances this operation iterates upon. | ||
24 | */ | ||
25 | public IInputKey getIteratedInputKey(); | ||
26 | |||
27 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/IPatternMatcherOperation.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/IPatternMatcherOperation.java new file mode 100644 index 00000000..f4a0f468 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/IPatternMatcherOperation.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.operations; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
12 | |||
13 | /** | ||
14 | * Marker interface for pattern matcher call operations, such as positive and negative pattern calls or match aggregators. | ||
15 | * | ||
16 | * @author Zoltan Ujhelyi | ||
17 | * @since 1.7 | ||
18 | */ | ||
19 | public interface IPatternMatcherOperation { | ||
20 | |||
21 | /** | ||
22 | * Returns the precomputed call information associated with the current operation | ||
23 | * @since 2.0 | ||
24 | */ | ||
25 | CallInformation getCallInformation(); | ||
26 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/ISearchOperation.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/ISearchOperation.java new file mode 100644 index 00000000..61519723 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/ISearchOperation.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, 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.interpreter.localsearch.operations; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.function.Function; | ||
13 | |||
14 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
15 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
16 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
17 | |||
18 | /** | ||
19 | * Represents a search operation executable by the LS engine. It is expected that an operation can be shared among | ||
20 | * multiple LS matchers, but the created executors are not. | ||
21 | * | ||
22 | * @author Zoltan Ujhelyi | ||
23 | * | ||
24 | */ | ||
25 | public interface ISearchOperation { | ||
26 | |||
27 | /** | ||
28 | * Initializes a new operation executor for the given operation. Repeated calls must return different executor | ||
29 | * instances. | ||
30 | * | ||
31 | * @since 2.0 | ||
32 | */ | ||
33 | public ISearchOperationExecutor createExecutor(); | ||
34 | |||
35 | /** | ||
36 | * | ||
37 | * @since 2.0 | ||
38 | * | ||
39 | */ | ||
40 | public interface ISearchOperationExecutor { | ||
41 | |||
42 | /** | ||
43 | * Returns the stateless operation this executor was initialized from | ||
44 | */ | ||
45 | ISearchOperation getOperation(); | ||
46 | |||
47 | /** | ||
48 | * During the execution of the corresponding plan, the onInitialize callback is evaluated before the execution of | ||
49 | * the operation may begin. Operations may use this method to initialize its internal data structures. | ||
50 | * | ||
51 | * @throws InterpreterRuntimeException | ||
52 | */ | ||
53 | void onInitialize(MatchingFrame frame, ISearchContext context); | ||
54 | |||
55 | /** | ||
56 | * After the execution of the operation failed and {@link #execute(MatchingFrame, ISearchContext)} returns false, the onBacktrack | ||
57 | * callback is evaluated. Operations may use this method to clean up any temporary structures, and make the | ||
58 | * operation ready for a new execution. | ||
59 | * | ||
60 | * @throws InterpreterRuntimeException | ||
61 | */ | ||
62 | void onBacktrack(MatchingFrame frame, ISearchContext context); | ||
63 | |||
64 | /** | ||
65 | * | ||
66 | * @param frame | ||
67 | * @param context | ||
68 | * @return true if successful, or false if backtracking needed | ||
69 | * @throws InterpreterRuntimeException | ||
70 | */ | ||
71 | boolean execute(MatchingFrame frame, ISearchContext context); | ||
72 | } | ||
73 | /** | ||
74 | * | ||
75 | * @return the ordered list of the variable numbers that are affected by the search operation | ||
76 | */ | ||
77 | List<Integer> getVariablePositions(); | ||
78 | |||
79 | /** | ||
80 | * Creates a string representation of the search operation by replacing the variable numbers according to the | ||
81 | * parameter function. It is expected that the provided function does return a non-null value for each variable | ||
82 | * index that is returned by {@link #getVariablePositions()}; otherwise a {@link NullPointerException} will be | ||
83 | * thrown during the calculation of the string. | ||
84 | * | ||
85 | * @since 2.0 | ||
86 | */ | ||
87 | String toString(Function<Integer, String> variableMapping); | ||
88 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/MatchingFrameValueProvider.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/MatchingFrameValueProvider.java new file mode 100644 index 00000000..b7a0dba0 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/MatchingFrameValueProvider.java | |||
@@ -0,0 +1,41 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, 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.interpreter.localsearch.operations; | ||
10 | |||
11 | import java.util.Map; | ||
12 | |||
13 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
14 | import tools.refinery.interpreter.matchers.psystem.IValueProvider; | ||
15 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
16 | |||
17 | /** | ||
18 | * | ||
19 | * | ||
20 | * @author Zoltan Ujhelyi | ||
21 | * | ||
22 | */ | ||
23 | public class MatchingFrameValueProvider implements IValueProvider { | ||
24 | |||
25 | final Map<String, Integer> nameMap; | ||
26 | final MatchingFrame frame; | ||
27 | |||
28 | public MatchingFrameValueProvider(MatchingFrame frame, Map<String, Integer> nameMap) { | ||
29 | super(); | ||
30 | this.frame = frame; | ||
31 | this.nameMap = nameMap; | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Object getValue(String variableName) { | ||
36 | Integer index = nameMap.get(variableName); | ||
37 | Preconditions.checkArgument(index != null, "Unknown parameter variable name"); | ||
38 | return frame.get(index); | ||
39 | } | ||
40 | |||
41 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/AggregatorCheck.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/AggregatorCheck.java new file mode 100644 index 00000000..440028ce --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/AggregatorCheck.java | |||
@@ -0,0 +1,116 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.operations.check; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | import java.util.Objects; | ||
14 | import java.util.function.Function; | ||
15 | import java.util.stream.Stream; | ||
16 | |||
17 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
18 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
19 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
20 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
21 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
22 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
23 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
24 | import tools.refinery.interpreter.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
25 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
26 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
27 | |||
28 | /** | ||
29 | * Calculates the aggregated value of a column based on the given {@link AggregatorConstraint} | ||
30 | * | ||
31 | * @author Balázs Grill | ||
32 | * @since 1.4 | ||
33 | * @noextend This class is not intended to be subclassed by clients. | ||
34 | */ | ||
35 | public class AggregatorCheck implements ISearchOperation, IPatternMatcherOperation { | ||
36 | |||
37 | private class Executor extends CheckOperationExecutor { | ||
38 | |||
39 | private final VolatileModifiableMaskedTuple maskedTuple; | ||
40 | private IQueryResultProvider matcher; | ||
41 | |||
42 | public Executor() { | ||
43 | super(); | ||
44 | this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask()); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public void onInitialize(MatchingFrame frame, ISearchContext context) { | ||
49 | super.onInitialize(frame, context); | ||
50 | maskedTuple.updateTuple(frame); | ||
51 | matcher = context.getMatcher(information.getCallWithAdornment()); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
56 | IMultisetAggregationOperator<?, ?, ?> operator = aggregator.getAggregator().getOperator(); | ||
57 | Object result = aggregate(operator, aggregator.getAggregatedColumn(), frame); | ||
58 | return result == null ? false : Objects.equals(frame.getValue(position), result); | ||
59 | } | ||
60 | |||
61 | @SuppressWarnings("unchecked") | ||
62 | private <Domain, Accumulator, AggregateResult> AggregateResult aggregate( | ||
63 | IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, int aggregatedColumn, | ||
64 | MatchingFrame initialFrame) { | ||
65 | maskedTuple.updateTuple(initialFrame); | ||
66 | final Stream<Domain> valueStream = matcher.getAllMatches(information.getParameterMask(), maskedTuple) | ||
67 | .map(match -> (Domain) match.get(aggregatedColumn)); | ||
68 | return operator.aggregateStream(valueStream); | ||
69 | } | ||
70 | |||
71 | @Override | ||
72 | public ISearchOperation getOperation() { | ||
73 | return AggregatorCheck.this; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | private final int position; | ||
78 | private final AggregatorConstraint aggregator; | ||
79 | private final CallInformation information; | ||
80 | |||
81 | /** | ||
82 | * @since 1.7 | ||
83 | */ | ||
84 | public AggregatorCheck(CallInformation information, AggregatorConstraint aggregator, int position) { | ||
85 | super(); | ||
86 | this.information = information; | ||
87 | this.position = position; | ||
88 | this.aggregator = aggregator; | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public ISearchOperationExecutor createExecutor() { | ||
93 | return new Executor(); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public List<Integer> getVariablePositions() { | ||
98 | return Collections.singletonList(position); | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public String toString() { | ||
103 | return toString(Object::toString); | ||
104 | } | ||
105 | |||
106 | @Override | ||
107 | public String toString(Function<Integer, String> variableMapping) { | ||
108 | return "check "+variableMapping.apply(position)+" = " + aggregator.getAggregator().getOperator().getName() + " find " + information.toString(variableMapping); | ||
109 | } | ||
110 | |||
111 | @Override | ||
112 | public CallInformation getCallInformation() { | ||
113 | return information; | ||
114 | } | ||
115 | |||
116 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/BinaryTransitiveClosureCheck.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/BinaryTransitiveClosureCheck.java new file mode 100644 index 00000000..078d0722 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/BinaryTransitiveClosureCheck.java | |||
@@ -0,0 +1,135 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.check; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.LinkedList; | ||
14 | import java.util.List; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Queue; | ||
17 | import java.util.Set; | ||
18 | import java.util.function.Function; | ||
19 | |||
20 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
21 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
22 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
23 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
24 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
25 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
26 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
27 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
28 | |||
29 | /** | ||
30 | * Checking for a transitive closure expressed as a local search pattern matcher. The matched pattern must have two | ||
31 | * parameters of the same model type. | ||
32 | * | ||
33 | * @author Zoltan Ujhelyi | ||
34 | * @noextend This class is not intended to be subclassed by clients. | ||
35 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
36 | * | ||
37 | */ | ||
38 | public class BinaryTransitiveClosureCheck implements ISearchOperation, IPatternMatcherOperation { | ||
39 | |||
40 | private class Executor extends CheckOperationExecutor { | ||
41 | |||
42 | @Override | ||
43 | public void onInitialize(MatchingFrame frame, ISearchContext context) { | ||
44 | super.onInitialize(frame, context); | ||
45 | matcher = context.getMatcher(information.getCallWithAdornment()); | ||
46 | // Note: second parameter is NOT bound during execution, but the first is | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
51 | if (checkReflexive(frame)) { | ||
52 | return true; | ||
53 | } | ||
54 | |||
55 | Object targetValue = frame.get(targetPosition); | ||
56 | Queue<Object> sourcesToEvaluate = new LinkedList<>(); | ||
57 | sourcesToEvaluate.add(frame.get(sourcePosition)); | ||
58 | Set<Object> sourceEvaluated = new HashSet<>(); | ||
59 | final Object[] mappedFrame = new Object[] {null, null}; | ||
60 | while (!sourcesToEvaluate.isEmpty()) { | ||
61 | Object currentValue = sourcesToEvaluate.poll(); | ||
62 | sourceEvaluated.add(currentValue); | ||
63 | mappedFrame[0] = currentValue; | ||
64 | for (Tuple match : (Iterable<Tuple>) () -> matcher.getAllMatches(mappedFrame).iterator()) { | ||
65 | Object foundTarget = match.get(1); | ||
66 | if (targetValue.equals(foundTarget)) { | ||
67 | return true; | ||
68 | } else if (!sourceEvaluated.contains(foundTarget)) { | ||
69 | sourcesToEvaluate.add(foundTarget); | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | return false; | ||
74 | } | ||
75 | |||
76 | protected boolean checkReflexive(MatchingFrame frame) { | ||
77 | return reflexive && Objects.equals(frame.get(sourcePosition), frame.get(targetPosition)); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public ISearchOperation getOperation() { | ||
82 | return BinaryTransitiveClosureCheck.this; | ||
83 | } | ||
84 | } | ||
85 | |||
86 | private final CallInformation information; | ||
87 | private IQueryResultProvider matcher; | ||
88 | private final int sourcePosition; | ||
89 | private final int targetPosition; | ||
90 | private final boolean reflexive; | ||
91 | |||
92 | /** | ||
93 | * The source position will be matched in the called pattern to the first parameter; while target to the second. | ||
94 | * </p> | ||
95 | * <strong>NOTE</strong>: the reflexive check call does not include the parameter type checks; appropriate type checks should be | ||
96 | * added as necessary by the operation compiler. | ||
97 | * | ||
98 | * @since 2.0 | ||
99 | */ | ||
100 | public BinaryTransitiveClosureCheck(CallInformation information, int sourcePosition, int targetPosition, boolean reflexive) { | ||
101 | super(); | ||
102 | this.sourcePosition = sourcePosition; | ||
103 | this.targetPosition = targetPosition; | ||
104 | this.information = information; | ||
105 | this.reflexive = reflexive; | ||
106 | } | ||
107 | |||
108 | @Override | ||
109 | public ISearchOperationExecutor createExecutor() { | ||
110 | return new Executor(); | ||
111 | } | ||
112 | |||
113 | @Override | ||
114 | public String toString() { | ||
115 | return toString(Object::toString); | ||
116 | } | ||
117 | |||
118 | @Override | ||
119 | public String toString(Function<Integer, String> variableMapping) { | ||
120 | String c = information.toString(variableMapping); | ||
121 | int p = c.indexOf('('); | ||
122 | String modifier = reflexive ? "*" : "+"; | ||
123 | return "check find "+c.substring(0, p)+ modifier +c.substring(p); | ||
124 | } | ||
125 | |||
126 | @Override | ||
127 | public List<Integer> getVariablePositions() { | ||
128 | return Arrays.asList(sourcePosition, targetPosition); | ||
129 | } | ||
130 | |||
131 | @Override | ||
132 | public CallInformation getCallInformation() { | ||
133 | return information; | ||
134 | } | ||
135 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CheckConstant.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CheckConstant.java new file mode 100644 index 00000000..4c39db6a --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CheckConstant.java | |||
@@ -0,0 +1,70 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.check; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | import java.util.function.Function; | ||
14 | |||
15 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
16 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
17 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
18 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
19 | |||
20 | /** | ||
21 | * This operation handles constants in search plans by checking if a variable is bound to a certain constant value. Such | ||
22 | * operations should be executed as early as possible during plan execution. | ||
23 | * | ||
24 | * @author Marton Bur | ||
25 | * @noextend This class is not intended to be subclassed by clients. | ||
26 | */ | ||
27 | public class CheckConstant implements ISearchOperation { | ||
28 | |||
29 | private class Executor extends CheckOperationExecutor { | ||
30 | |||
31 | @Override | ||
32 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
33 | return frame.get(position).equals(value); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public ISearchOperation getOperation() { | ||
38 | return CheckConstant.this; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | private int position; | ||
43 | private Object value; | ||
44 | |||
45 | public CheckConstant(int position, Object value) { | ||
46 | this.position = position; | ||
47 | this.value = value; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public ISearchOperationExecutor createExecutor() { | ||
52 | return new Executor(); | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public List<Integer> getVariablePositions() { | ||
57 | return Collections.singletonList(position); | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public String toString() { | ||
62 | return toString(Object::toString); | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public String toString(Function<Integer, String> variableMapping) { | ||
67 | return "check constant "+variableMapping.apply(position)+"='"+value+"'"; | ||
68 | } | ||
69 | |||
70 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CheckPositivePatternCall.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CheckPositivePatternCall.java new file mode 100644 index 00000000..8d9ae0e2 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CheckPositivePatternCall.java | |||
@@ -0,0 +1,96 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.localsearch.operations.check; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.function.Function; | ||
13 | |||
14 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
15 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
16 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
17 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
18 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
19 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
20 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
21 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
22 | |||
23 | /** | ||
24 | * @author Grill Balázs | ||
25 | * @since 1.4 | ||
26 | * @noextend This class is not intended to be subclassed by clients. | ||
27 | */ | ||
28 | public class CheckPositivePatternCall implements ISearchOperation, IPatternMatcherOperation { | ||
29 | |||
30 | private class Executor extends CheckOperationExecutor { | ||
31 | |||
32 | private final VolatileModifiableMaskedTuple maskedTuple; | ||
33 | private IQueryResultProvider matcher; | ||
34 | |||
35 | public Executor() { | ||
36 | super(); | ||
37 | this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask()); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public void onInitialize(MatchingFrame frame, ISearchContext context) { | ||
42 | super.onInitialize(frame, context); | ||
43 | maskedTuple.updateTuple(frame); | ||
44 | matcher = context.getMatcher(information.getCallWithAdornment()); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @since 1.5 | ||
49 | */ | ||
50 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
51 | return matcher.hasMatch(information.getParameterMask(), maskedTuple); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public ISearchOperation getOperation() { | ||
56 | return CheckPositivePatternCall.this; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | private final CallInformation information; | ||
61 | |||
62 | |||
63 | /** | ||
64 | * @since 1.7 | ||
65 | */ | ||
66 | public CheckPositivePatternCall(CallInformation information) { | ||
67 | super(); | ||
68 | this.information = information; | ||
69 | |||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public ISearchOperationExecutor createExecutor() { | ||
74 | return new Executor(); | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public List<Integer> getVariablePositions() { | ||
79 | return information.getVariablePositions(); | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public String toString() { | ||
84 | return toString(Object::toString); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public String toString(Function<Integer, String> variableMapping) { | ||
89 | return "check find "+information.toString(variableMapping); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public CallInformation getCallInformation() { | ||
94 | return information; | ||
95 | } | ||
96 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CountCheck.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CountCheck.java new file mode 100644 index 00000000..c8e77a70 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/CountCheck.java | |||
@@ -0,0 +1,92 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.check; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.List; | ||
13 | import java.util.function.Function; | ||
14 | |||
15 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
16 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
17 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
18 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
19 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
20 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
21 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
22 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
23 | |||
24 | /** | ||
25 | * Calculates the count of matches for a called matcher | ||
26 | * | ||
27 | * @author Zoltan Ujhelyi | ||
28 | * @noextend This class is not intended to be subclassed by clients. | ||
29 | */ | ||
30 | public class CountCheck implements ISearchOperation, IPatternMatcherOperation { | ||
31 | |||
32 | private class Executor extends CheckOperationExecutor { | ||
33 | |||
34 | private final VolatileModifiableMaskedTuple maskedTuple; | ||
35 | private IQueryResultProvider matcher; | ||
36 | |||
37 | public Executor() { | ||
38 | maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask()); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public void onInitialize(MatchingFrame frame, ISearchContext context) { | ||
43 | super.onInitialize(frame, context); | ||
44 | maskedTuple.updateTuple(frame); | ||
45 | matcher = context.getMatcher(information.getCallWithAdornment()); | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
50 | int count = matcher.countMatches(information.getParameterMask(), maskedTuple); | ||
51 | return ((Integer)frame.getValue(position)) == count; | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public ISearchOperation getOperation() { | ||
56 | return CountCheck.this; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | private final int position; | ||
61 | private final CallInformation information; | ||
62 | |||
63 | /** | ||
64 | * @since 1.7 | ||
65 | */ | ||
66 | public CountCheck(CallInformation information, int position) { | ||
67 | super(); | ||
68 | this.information = information; | ||
69 | this.position = position; | ||
70 | } | ||
71 | |||
72 | |||
73 | @Override | ||
74 | public ISearchOperationExecutor createExecutor() { | ||
75 | return new Executor(); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public List<Integer> getVariablePositions() { | ||
80 | return Collections.singletonList(position); | ||
81 | } | ||
82 | |||
83 | @Override | ||
84 | public String toString(Function<Integer, String> variableMapping) { | ||
85 | return "check "+variableMapping.apply(position)+" = count find "+ information.toString(variableMapping); | ||
86 | } | ||
87 | |||
88 | @Override | ||
89 | public CallInformation getCallInformation() { | ||
90 | return information; | ||
91 | } | ||
92 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/ExpressionCheck.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/ExpressionCheck.java new file mode 100644 index 00000000..5e34a7a7 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/ExpressionCheck.java | |||
@@ -0,0 +1,78 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.check; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.function.Function; | ||
15 | |||
16 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
17 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
18 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
19 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
20 | import tools.refinery.interpreter.localsearch.operations.MatchingFrameValueProvider; | ||
21 | import tools.refinery.interpreter.matchers.psystem.IExpressionEvaluator; | ||
22 | |||
23 | /** | ||
24 | * @author Zoltan Ujhelyi | ||
25 | * @noextend This class is not intended to be subclassed by clients. | ||
26 | */ | ||
27 | public class ExpressionCheck implements ISearchOperation { | ||
28 | |||
29 | private class Executor extends CheckOperationExecutor { | ||
30 | |||
31 | @Override | ||
32 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
33 | try { | ||
34 | boolean result = (Boolean) evaluator.evaluateExpression(new MatchingFrameValueProvider(frame, nameMap)); | ||
35 | return result; | ||
36 | } catch (Exception e) { | ||
37 | context.getLogger().warn("Error while evaluating expression", e); | ||
38 | return false; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public ISearchOperation getOperation() { | ||
44 | return ExpressionCheck.this; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | IExpressionEvaluator evaluator; | ||
49 | Map<String, Integer> nameMap; | ||
50 | |||
51 | public ExpressionCheck(IExpressionEvaluator evaluator, Map<String, Integer> nameMap) { | ||
52 | super(); | ||
53 | this.evaluator = evaluator; | ||
54 | this.nameMap = nameMap; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public ISearchOperationExecutor createExecutor() { | ||
59 | return new Executor(); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public List<Integer> getVariablePositions() { | ||
64 | // XXX not sure if this is the correct implementation to get the affected variable indicies | ||
65 | return new ArrayList<>(nameMap.values()); | ||
66 | } | ||
67 | |||
68 | @Override | ||
69 | public String toString() { | ||
70 | return toString(Object::toString); | ||
71 | } | ||
72 | |||
73 | @Override | ||
74 | public String toString(Function<Integer, String> variableMapping) { | ||
75 | return "check expression "+evaluator.getShortDescription(); | ||
76 | } | ||
77 | |||
78 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/ExpressionEvalCheck.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/ExpressionEvalCheck.java new file mode 100644 index 00000000..f4b6f903 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/ExpressionEvalCheck.java | |||
@@ -0,0 +1,95 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.localsearch.operations.check; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | import java.util.function.Function; | ||
16 | |||
17 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
18 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
19 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
20 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
21 | import tools.refinery.interpreter.localsearch.operations.MatchingFrameValueProvider; | ||
22 | import tools.refinery.interpreter.matchers.psystem.IExpressionEvaluator; | ||
23 | |||
24 | /** | ||
25 | * @author Grill Balázs | ||
26 | * @since 1.3 | ||
27 | * @noextend This class is not intended to be subclassed by clients. | ||
28 | */ | ||
29 | public class ExpressionEvalCheck implements ISearchOperation { | ||
30 | |||
31 | private class Executor extends CheckOperationExecutor { | ||
32 | |||
33 | @Override | ||
34 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
35 | try { | ||
36 | Object result = evaluator.evaluateExpression(new MatchingFrameValueProvider(frame, nameMap)); | ||
37 | if (!unwind && result != null) { | ||
38 | Object currentValue = frame.get(outputPosition); | ||
39 | return result.equals(currentValue); | ||
40 | } else if (unwind && result instanceof Set<?>) { | ||
41 | Object currentValue = frame.get(outputPosition); | ||
42 | return ((Set<?>)result).contains(currentValue); | ||
43 | } | ||
44 | } catch (Exception e) { | ||
45 | context.getLogger().warn("Error while evaluating expression", e); | ||
46 | } | ||
47 | return false; | ||
48 | } | ||
49 | |||
50 | @Override | ||
51 | public ISearchOperation getOperation() { | ||
52 | return ExpressionEvalCheck.this; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | private final int outputPosition; | ||
57 | private final IExpressionEvaluator evaluator; | ||
58 | private final Map<String, Integer> nameMap; | ||
59 | private final boolean unwind; | ||
60 | |||
61 | public ExpressionEvalCheck(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, int position) { | ||
62 | this(evaluator, nameMap, false, position); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * @since 2.7 | ||
67 | */ | ||
68 | public ExpressionEvalCheck(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, boolean unwind, int position) { | ||
69 | this.evaluator = evaluator; | ||
70 | this.nameMap = nameMap; | ||
71 | this.unwind = unwind; | ||
72 | this.outputPosition = position; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public ISearchOperationExecutor createExecutor() { | ||
77 | return new Executor(); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public List<Integer> getVariablePositions() { | ||
82 | // XXX not sure if this is the correct implementation to get the affected variable indicies | ||
83 | return new ArrayList<>(nameMap.values()); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public String toString() { | ||
88 | return toString(Object::toString); | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public String toString(Function<Integer, String> variableMapping) { | ||
93 | return "check "+variableMapping.apply(outputPosition)+" = expression "+evaluator.getShortDescription(); | ||
94 | } | ||
95 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/InequalityCheck.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/InequalityCheck.java new file mode 100644 index 00000000..4383d5a7 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/InequalityCheck.java | |||
@@ -0,0 +1,77 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.check; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.List; | ||
13 | import java.util.function.Function; | ||
14 | |||
15 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
16 | import tools.refinery.interpreter.localsearch.exceptions.LocalSearchException; | ||
17 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
18 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
19 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
20 | |||
21 | /** | ||
22 | * @author Zoltan Ujhelyi | ||
23 | * @noextend This class is not intended to be subclassed by clients. | ||
24 | */ | ||
25 | public class InequalityCheck implements ISearchOperation { | ||
26 | |||
27 | private class Executor extends CheckOperationExecutor { | ||
28 | |||
29 | @Override | ||
30 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
31 | Object source = frame.getValue(sourceLocation); | ||
32 | Object target = frame.getValue(targetLocation); | ||
33 | if (source == null) { | ||
34 | throw new LocalSearchException("Source not bound."); | ||
35 | } | ||
36 | if (target == null) { | ||
37 | throw new LocalSearchException("Target not bound"); | ||
38 | } | ||
39 | return !source.equals(target); | ||
40 | } | ||
41 | |||
42 | @Override | ||
43 | public ISearchOperation getOperation() { | ||
44 | return InequalityCheck.this; | ||
45 | } | ||
46 | } | ||
47 | |||
48 | int sourceLocation; | ||
49 | int targetLocation; | ||
50 | |||
51 | public InequalityCheck(int sourceLocation, int targetLocation) { | ||
52 | super(); | ||
53 | this.sourceLocation = sourceLocation; | ||
54 | this.targetLocation = targetLocation; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public ISearchOperationExecutor createExecutor() { | ||
59 | return new Executor(); | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public String toString() { | ||
64 | return toString(Object::toString); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public String toString(Function<Integer, String> variableMapping) { | ||
69 | return "check "+variableMapping.apply(sourceLocation)+" != "+variableMapping.apply(targetLocation); | ||
70 | } | ||
71 | |||
72 | @Override | ||
73 | public List<Integer> getVariablePositions() { | ||
74 | return Arrays.asList(sourceLocation, targetLocation); | ||
75 | } | ||
76 | |||
77 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/NACOperation.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/NACOperation.java new file mode 100644 index 00000000..0921678e --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/check/NACOperation.java | |||
@@ -0,0 +1,89 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.check; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.function.Function; | ||
13 | |||
14 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
15 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
16 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
17 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
18 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
19 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
20 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
21 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
22 | |||
23 | /** | ||
24 | * @author Zoltan Ujhelyi | ||
25 | * @noextend This class is not intended to be subclassed by clients. | ||
26 | */ | ||
27 | public class NACOperation implements ISearchOperation, IPatternMatcherOperation { | ||
28 | |||
29 | private class Executor extends CheckOperationExecutor { | ||
30 | private final VolatileModifiableMaskedTuple maskedTuple; | ||
31 | private IQueryResultProvider matcher; | ||
32 | |||
33 | private Executor() { | ||
34 | this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask()); | ||
35 | } | ||
36 | |||
37 | @Override | ||
38 | public void onInitialize(MatchingFrame frame, ISearchContext context) { | ||
39 | super.onInitialize(frame, context); | ||
40 | maskedTuple.updateTuple(frame); | ||
41 | matcher = context.getMatcher(information.getCallWithAdornment()); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
46 | return !matcher.hasMatch(information.getParameterMask(), maskedTuple); | ||
47 | } | ||
48 | |||
49 | @Override | ||
50 | public ISearchOperation getOperation() { | ||
51 | return NACOperation.this; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | private final CallInformation information; | ||
56 | |||
57 | /** | ||
58 | * @since 1.7 | ||
59 | */ | ||
60 | public NACOperation(CallInformation information) { | ||
61 | super(); | ||
62 | this.information = information; | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public ISearchOperationExecutor createExecutor() { | ||
67 | return new Executor(); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public String toString() { | ||
72 | return toString(Object::toString); | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public String toString(Function<Integer, String> variableMapping) { | ||
77 | return "check neg find "+information.toString(variableMapping); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public List<Integer> getVariablePositions() { | ||
82 | return information.getVariablePositions(); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public CallInformation getCallInformation() { | ||
87 | return information; | ||
88 | } | ||
89 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/AggregatorExtend.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/AggregatorExtend.java new file mode 100644 index 00000000..19d0a41d --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/AggregatorExtend.java | |||
@@ -0,0 +1,106 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.operations.extend; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.List; | ||
15 | import java.util.function.Function; | ||
16 | import java.util.stream.Stream; | ||
17 | |||
18 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
19 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
20 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
21 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
22 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
23 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
24 | import tools.refinery.interpreter.matchers.psystem.aggregations.IMultisetAggregationOperator; | ||
25 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
26 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
27 | |||
28 | /** | ||
29 | * Calculates the aggregated value of a column based on the given {@link AggregatorConstraint} | ||
30 | * | ||
31 | * @author Balázs Grill | ||
32 | * @since 1.4 | ||
33 | */ | ||
34 | public class AggregatorExtend implements ISearchOperation, IPatternMatcherOperation{ | ||
35 | |||
36 | private class Executor extends SingleValueExtendOperationExecutor<Object> { | ||
37 | |||
38 | private final VolatileModifiableMaskedTuple maskedTuple; | ||
39 | private IQueryResultProvider matcher; | ||
40 | |||
41 | public Executor(int position) { | ||
42 | super(position); | ||
43 | this.maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask()); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) { | ||
48 | maskedTuple.updateTuple(frame); | ||
49 | matcher = context.getMatcher(information.getCallWithAdornment()); | ||
50 | Object aggregate = aggregate(aggregator.getAggregator().getOperator(), aggregator.getAggregatedColumn()); | ||
51 | return aggregate == null ? Collections.emptyIterator() : Collections.singletonList(aggregate).iterator(); | ||
52 | |||
53 | } | ||
54 | |||
55 | @SuppressWarnings("unchecked") | ||
56 | private <Domain, Accumulator, AggregateResult> AggregateResult aggregate( | ||
57 | IMultisetAggregationOperator<Domain, Accumulator, AggregateResult> operator, int aggregatedColumn) { | ||
58 | final Stream<Domain> valueStream = matcher.getAllMatches(information.getParameterMask(), maskedTuple) | ||
59 | .map(match -> (Domain) match.get(aggregatedColumn)); | ||
60 | return operator.aggregateStream(valueStream); | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public ISearchOperation getOperation() { | ||
65 | return AggregatorExtend.this; | ||
66 | } | ||
67 | } | ||
68 | |||
69 | private final AggregatorConstraint aggregator; | ||
70 | private final CallInformation information; | ||
71 | private final int position; | ||
72 | |||
73 | /** | ||
74 | * @since 1.7 | ||
75 | */ | ||
76 | public AggregatorExtend(CallInformation information, AggregatorConstraint aggregator, int position) { | ||
77 | this.aggregator = aggregator; | ||
78 | this.information = information; | ||
79 | this.position = position; | ||
80 | } | ||
81 | |||
82 | @Override | ||
83 | public ISearchOperationExecutor createExecutor() { | ||
84 | return new Executor(position); | ||
85 | } | ||
86 | |||
87 | @Override | ||
88 | public List<Integer> getVariablePositions() { | ||
89 | return Arrays.asList(position); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public String toString() { | ||
94 | return toString(Object::toString); | ||
95 | } | ||
96 | |||
97 | @Override | ||
98 | public String toString(Function<Integer, String> variableMapping) { | ||
99 | return "extend -"+variableMapping.apply(position)+" = " + aggregator.getAggregator().getOperator().getName()+" find " + information.toString(variableMapping); | ||
100 | } | ||
101 | |||
102 | @Override | ||
103 | public CallInformation getCallInformation() { | ||
104 | return information; | ||
105 | } | ||
106 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/CountOperation.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/CountOperation.java new file mode 100644 index 00000000..1451f92d --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/CountOperation.java | |||
@@ -0,0 +1,88 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.extend; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.Iterator; | ||
13 | import java.util.List; | ||
14 | import java.util.function.Function; | ||
15 | |||
16 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
17 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
18 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
19 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
20 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
21 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
22 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
23 | |||
24 | /** | ||
25 | * Calculates the count of matches for a called matcher | ||
26 | * | ||
27 | * @author Zoltan Ujhelyi | ||
28 | */ | ||
29 | public class CountOperation implements ISearchOperation, IPatternMatcherOperation{ | ||
30 | |||
31 | private class Executor extends SingleValueExtendOperationExecutor<Integer> { | ||
32 | private final VolatileModifiableMaskedTuple maskedTuple; | ||
33 | private IQueryResultProvider matcher; | ||
34 | |||
35 | public Executor(int position) { | ||
36 | super(position); | ||
37 | maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask()); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | public Iterator<Integer> getIterator(MatchingFrame frame, ISearchContext context) { | ||
42 | matcher = context.getMatcher(information.getCallWithAdornment()); | ||
43 | maskedTuple.updateTuple(frame); | ||
44 | return Collections.singletonList(matcher.countMatches(information.getParameterMask(), maskedTuple)).iterator(); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public ISearchOperation getOperation() { | ||
49 | return CountOperation.this; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | private final CallInformation information; | ||
54 | private final int position; | ||
55 | |||
56 | /** | ||
57 | * @since 1.7 | ||
58 | */ | ||
59 | public CountOperation(CallInformation information, int position) { | ||
60 | this.information = information; | ||
61 | this.position = position; | ||
62 | } | ||
63 | |||
64 | @Override | ||
65 | public ISearchOperationExecutor createExecutor() { | ||
66 | return new Executor(position); | ||
67 | } | ||
68 | |||
69 | @Override | ||
70 | public List<Integer> getVariablePositions() { | ||
71 | return information.getVariablePositions(); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public String toString() { | ||
76 | return toString(Object::toString); | ||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public String toString(Function<Integer, String> variableMapping) { | ||
81 | return "extend -"+variableMapping.apply(position)+" = count find " + information.toString(variableMapping); | ||
82 | } | ||
83 | |||
84 | @Override | ||
85 | public CallInformation getCallInformation() { | ||
86 | return information; | ||
87 | } | ||
88 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExpressionEval.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExpressionEval.java new file mode 100644 index 00000000..bdd16265 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExpressionEval.java | |||
@@ -0,0 +1,104 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.extend; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | import java.util.function.Function; | ||
18 | |||
19 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
20 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
21 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
22 | import tools.refinery.interpreter.localsearch.operations.MatchingFrameValueProvider; | ||
23 | import tools.refinery.interpreter.matchers.psystem.IExpressionEvaluator; | ||
24 | |||
25 | /** | ||
26 | * Calculates the result of an expression and stores it inside a variable for future reference. | ||
27 | * | ||
28 | * @author Zoltan Ujhelyi | ||
29 | * | ||
30 | */ | ||
31 | public class ExpressionEval implements ISearchOperation { | ||
32 | |||
33 | private class Executor extends SingleValueExtendOperationExecutor<Object> { | ||
34 | |||
35 | public Executor(int position) { | ||
36 | super(position); | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) { | ||
41 | try { | ||
42 | Object result = evaluator.evaluateExpression(new MatchingFrameValueProvider(frame, nameMap)); | ||
43 | if (!unwind && result != null){ | ||
44 | return Collections.singletonList(result).iterator(); | ||
45 | } else if (unwind && result instanceof Set<?>) { | ||
46 | return ((Set<?>)result).iterator(); | ||
47 | } else { | ||
48 | return Collections.emptyIterator(); | ||
49 | } | ||
50 | } catch (Exception e) { | ||
51 | context.getLogger().warn("Error while evaluating expression", e); | ||
52 | return Collections.emptyIterator(); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public ISearchOperation getOperation() { | ||
58 | return ExpressionEval.this; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | private final IExpressionEvaluator evaluator; | ||
63 | private final boolean unwind; | ||
64 | private final Map<String, Integer> nameMap; | ||
65 | private final int position; | ||
66 | |||
67 | public ExpressionEval(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, int position) { | ||
68 | this(evaluator, nameMap, false, position); | ||
69 | } | ||
70 | |||
71 | /** | ||
72 | * @since 2.7 | ||
73 | */ | ||
74 | public ExpressionEval(IExpressionEvaluator evaluator, Map<String, Integer> nameMap, boolean unwind, int position) { | ||
75 | this.evaluator = evaluator; | ||
76 | this.nameMap = nameMap; | ||
77 | this.unwind = unwind; | ||
78 | this.position = position; | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public String toString() { | ||
83 | return toString(Object::toString); | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public String toString(Function<Integer, String> variableMapping) { | ||
88 | return "extend -"+variableMapping.apply(position)+" = expression "+evaluator.getShortDescription(); | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public ISearchOperationExecutor createExecutor() { | ||
93 | return new Executor(position); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public List<Integer> getVariablePositions() { | ||
98 | // XXX not sure if this is the correct implementation to get the affected variable indicies | ||
99 | List<Integer> variables = new ArrayList<>(); | ||
100 | variables.addAll(nameMap.values()); | ||
101 | return variables; | ||
102 | } | ||
103 | |||
104 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendBinaryTransitiveClosure.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendBinaryTransitiveClosure.java new file mode 100644 index 00000000..ae6bc30c --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendBinaryTransitiveClosure.java | |||
@@ -0,0 +1,185 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2013, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.extend; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.HashSet; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.LinkedList; | ||
15 | import java.util.List; | ||
16 | import java.util.Queue; | ||
17 | import java.util.Set; | ||
18 | import java.util.function.Function; | ||
19 | |||
20 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
21 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
22 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
23 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
24 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
25 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
26 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
27 | |||
28 | /** | ||
29 | * Checking for a transitive closure expressed as a local search pattern matcher. The matched pattern must have two | ||
30 | * parameters of the same model type. | ||
31 | * | ||
32 | * @author Zoltan Ujhelyi | ||
33 | * @since 1.7 | ||
34 | * | ||
35 | */ | ||
36 | public abstract class ExtendBinaryTransitiveClosure implements ISearchOperation, IPatternMatcherOperation { | ||
37 | |||
38 | private class Executor extends SingleValueExtendOperationExecutor<Object> { | ||
39 | |||
40 | public Executor(int position) { | ||
41 | super(position); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) { | ||
46 | // Note: second parameter is NOT bound during execution, but the first is | ||
47 | IQueryResultProvider matcher = context.getMatcher(information.getCallWithAdornment()); | ||
48 | |||
49 | Queue<Object> seedsToEvaluate = new LinkedList<>(); | ||
50 | final Object seedValue = frame.get(seedPosition); | ||
51 | seedsToEvaluate.add(seedValue); | ||
52 | Set<Object> seedsEvaluated = new HashSet<>(); | ||
53 | Set<Object> targetsFound = new HashSet<>(); | ||
54 | if (reflexive) { | ||
55 | targetsFound.add(seedValue); | ||
56 | } | ||
57 | |||
58 | while(!seedsToEvaluate.isEmpty()) { | ||
59 | Object currentValue = seedsToEvaluate.poll(); | ||
60 | seedsEvaluated.add(currentValue); | ||
61 | final Object[] mappedFrame = calculateCallFrame(currentValue); | ||
62 | matcher.getAllMatches(mappedFrame).forEach(match -> { | ||
63 | Object foundTarget = getTarget(match); | ||
64 | targetsFound.add(foundTarget); | ||
65 | if (!seedsEvaluated.contains(foundTarget)) { | ||
66 | seedsToEvaluate.add(foundTarget); | ||
67 | } | ||
68 | }); | ||
69 | } | ||
70 | |||
71 | return targetsFound.iterator(); | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public ISearchOperation getOperation() { | ||
76 | return ExtendBinaryTransitiveClosure.this; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | /** | ||
81 | * Calculates the transitive closure of a pattern match in a forward direction (first parameter bound, second | ||
82 | * unbound). | ||
83 | * </p> | ||
84 | * <strong>Note</strong>: In case the call is reflexive, it is expected that the bound parameter already matches the universe type of the call. | ||
85 | * | ||
86 | * @since 1.7 | ||
87 | */ | ||
88 | public static class Forward extends ExtendBinaryTransitiveClosure { | ||
89 | |||
90 | private Object[] seedFrame = new Object[2]; | ||
91 | |||
92 | /** | ||
93 | * @since 2.0 | ||
94 | */ | ||
95 | public Forward(CallInformation information, int sourcePosition, int targetPosition, boolean reflexive) { | ||
96 | super(information, sourcePosition, targetPosition, reflexive); | ||
97 | } | ||
98 | |||
99 | protected Object[] calculateCallFrame(Object seed) { | ||
100 | seedFrame[0] = seed; | ||
101 | seedFrame[1] = null; | ||
102 | return seedFrame; | ||
103 | } | ||
104 | |||
105 | protected Object getTarget(Tuple frame) { | ||
106 | return frame.get(1); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Calculates the transitive closure of a pattern match in a backward direction (first parameter unbound, second | ||
112 | * bound) | ||
113 | * </p> | ||
114 | * <strong>Note</strong>: In case the call is reflexive, it is expected that the bound parameter already matches the universe type of the call. | ||
115 | * | ||
116 | * @since 2.0 | ||
117 | */ | ||
118 | public static class Backward extends ExtendBinaryTransitiveClosure { | ||
119 | private Object[] seedFrame = new Object[2]; | ||
120 | |||
121 | /** | ||
122 | * @since 2.0 | ||
123 | */ | ||
124 | public Backward(CallInformation information, int sourcePosition, int targetPosition, boolean reflexive) { | ||
125 | super(information, targetPosition, sourcePosition, reflexive); | ||
126 | } | ||
127 | |||
128 | protected Object[] calculateCallFrame(Object seed) { | ||
129 | seedFrame[0] = null; | ||
130 | seedFrame[1] = seed; | ||
131 | return seedFrame; | ||
132 | } | ||
133 | |||
134 | protected Object getTarget(Tuple frame) { | ||
135 | return frame.get(0); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | private final int seedPosition; | ||
140 | private final int targetPosition; | ||
141 | private final CallInformation information; | ||
142 | private final boolean reflexive; | ||
143 | |||
144 | /** | ||
145 | * The source position will be matched in the called pattern to the first parameter; while target to the second. | ||
146 | * @since 2.0 | ||
147 | */ | ||
148 | protected ExtendBinaryTransitiveClosure(CallInformation information, int seedPosition, int targetPosition, boolean reflexive) { | ||
149 | this.information = information; | ||
150 | this.seedPosition = seedPosition; | ||
151 | this.targetPosition = targetPosition; | ||
152 | this.reflexive = reflexive; | ||
153 | } | ||
154 | |||
155 | protected abstract Object[] calculateCallFrame(Object seed); | ||
156 | |||
157 | protected abstract Object getTarget(Tuple frame); | ||
158 | |||
159 | @Override | ||
160 | public ISearchOperationExecutor createExecutor() { | ||
161 | return new Executor(targetPosition); | ||
162 | } | ||
163 | |||
164 | @Override | ||
165 | public String toString() { | ||
166 | return toString(Object::toString); | ||
167 | } | ||
168 | |||
169 | @Override | ||
170 | public String toString(Function<Integer, String> variableMapping) { | ||
171 | String c = information.toString(variableMapping); | ||
172 | int p = c.indexOf('('); | ||
173 | return "extend find " + c.substring(0, p) + "+" + c.substring(p); | ||
174 | } | ||
175 | |||
176 | @Override | ||
177 | public List<Integer> getVariablePositions() { | ||
178 | return Arrays.asList(seedPosition, targetPosition); | ||
179 | } | ||
180 | |||
181 | @Override | ||
182 | public CallInformation getCallInformation() { | ||
183 | return information; | ||
184 | } | ||
185 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendConstant.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendConstant.java new file mode 100644 index 00000000..69221662 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendConstant.java | |||
@@ -0,0 +1,75 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.operations.extend; | ||
10 | |||
11 | import java.util.Arrays; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.List; | ||
15 | import java.util.function.Function; | ||
16 | |||
17 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
18 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
19 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
20 | |||
21 | /** | ||
22 | * This operation handles constants in search plans by binding a variable to a constant value. Such operations should be | ||
23 | * executed as early as possible during plan execution. | ||
24 | * | ||
25 | * @author Marton Bur | ||
26 | * | ||
27 | */ | ||
28 | public class ExtendConstant implements ISearchOperation { | ||
29 | |||
30 | private class Executor extends SingleValueExtendOperationExecutor<Object> { | ||
31 | |||
32 | public Executor(int position) { | ||
33 | super(position); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public Iterator<?> getIterator(MatchingFrame frame, ISearchContext context) { | ||
38 | return Collections.singletonList(value).iterator(); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | public ISearchOperation getOperation() { | ||
43 | return ExtendConstant.this; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | private final Object value; | ||
48 | private final int position; | ||
49 | |||
50 | public ExtendConstant(int position, Object value) { | ||
51 | this.position = position; | ||
52 | this.value = value; | ||
53 | } | ||
54 | |||
55 | @Override | ||
56 | public ISearchOperationExecutor createExecutor() { | ||
57 | return new Executor(position); | ||
58 | } | ||
59 | |||
60 | @Override | ||
61 | public List<Integer> getVariablePositions() { | ||
62 | return Arrays.asList(position); | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public String toString() { | ||
67 | return toString(Object::toString); | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public String toString(Function<Integer, String> variableMapping) { | ||
72 | return "extend constant -"+variableMapping.apply(position)+"='"+value+"'"; | ||
73 | } | ||
74 | |||
75 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendPositivePatternCall.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendPositivePatternCall.java new file mode 100644 index 00000000..b0bd2dcf --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/ExtendPositivePatternCall.java | |||
@@ -0,0 +1,118 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs | ||
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.localsearch.operations.extend; | ||
10 | |||
11 | import java.util.Iterator; | ||
12 | import java.util.List; | ||
13 | import java.util.function.Function; | ||
14 | |||
15 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
16 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
17 | import tools.refinery.interpreter.localsearch.operations.ExtendOperationExecutor; | ||
18 | import tools.refinery.interpreter.localsearch.operations.IPatternMatcherOperation; | ||
19 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
20 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
21 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
22 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
23 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
24 | import tools.refinery.interpreter.matchers.tuple.VolatileModifiableMaskedTuple; | ||
25 | |||
26 | /** | ||
27 | * @author Grill Balázs | ||
28 | * @since 1.4 | ||
29 | * | ||
30 | */ | ||
31 | public class ExtendPositivePatternCall implements ISearchOperation, IPatternMatcherOperation { | ||
32 | |||
33 | private class Executor extends ExtendOperationExecutor<Tuple> { | ||
34 | private final VolatileModifiableMaskedTuple maskedTuple; | ||
35 | |||
36 | public Executor() { | ||
37 | maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask()); | ||
38 | } | ||
39 | |||
40 | @Override | ||
41 | protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) { | ||
42 | maskedTuple.updateTuple(frame); | ||
43 | IQueryResultProvider matcher = context.getMatcher(information.getCallWithAdornment()); | ||
44 | return matcher.getAllMatches(information.getParameterMask(), maskedTuple).iterator(); | ||
45 | } | ||
46 | |||
47 | /** | ||
48 | * @since 2.0 | ||
49 | */ | ||
50 | @Override | ||
51 | protected boolean fillInValue(Tuple result, MatchingFrame frame, ISearchContext context) { | ||
52 | TupleMask mask = information.getFullFrameMask(); | ||
53 | // The first loop clears out the elements from a possible previous iteration | ||
54 | for(int i : information.getFreeParameterIndices()) { | ||
55 | mask.set(frame, i, null); | ||
56 | } | ||
57 | for(int i : information.getFreeParameterIndices()) { | ||
58 | Object oldValue = mask.getValue(frame, i); | ||
59 | Object valueToFill = result.get(i); | ||
60 | if (oldValue != null && !oldValue.equals(valueToFill)){ | ||
61 | // If the inverse map contains more than one values for the same key, it means that these arguments are unified by the caller. | ||
62 | // In this case if the callee assigns different values the frame shall be dropped | ||
63 | return false; | ||
64 | } | ||
65 | mask.set(frame, i, valueToFill); | ||
66 | } | ||
67 | return true; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | protected void cleanup(MatchingFrame frame, ISearchContext context) { | ||
72 | TupleMask mask = information.getFullFrameMask(); | ||
73 | for(int i : information.getFreeParameterIndices()){ | ||
74 | mask.set(frame, i, null); | ||
75 | } | ||
76 | |||
77 | } | ||
78 | |||
79 | @Override | ||
80 | public ISearchOperation getOperation() { | ||
81 | return ExtendPositivePatternCall.this; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | private final CallInformation information; | ||
86 | |||
87 | /** | ||
88 | * @since 1.7 | ||
89 | */ | ||
90 | public ExtendPositivePatternCall(CallInformation information) { | ||
91 | this.information = information; | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public ISearchOperationExecutor createExecutor() { | ||
96 | return new Executor(); | ||
97 | } | ||
98 | |||
99 | @Override | ||
100 | public List<Integer> getVariablePositions() { | ||
101 | return information.getVariablePositions(); | ||
102 | } | ||
103 | |||
104 | @Override | ||
105 | public String toString() { | ||
106 | return toString(Object::toString); | ||
107 | } | ||
108 | |||
109 | @Override | ||
110 | public String toString(Function<Integer, String> variableMapping) { | ||
111 | return "extend find " + information.toString(variableMapping); | ||
112 | } | ||
113 | |||
114 | @Override | ||
115 | public CallInformation getCallInformation() { | ||
116 | return information; | ||
117 | } | ||
118 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/SingleValueExtendOperationExecutor.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/SingleValueExtendOperationExecutor.java new file mode 100644 index 00000000..31b4c294 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/extend/SingleValueExtendOperationExecutor.java | |||
@@ -0,0 +1,40 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.operations.extend; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
12 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
13 | import tools.refinery.interpreter.localsearch.operations.ExtendOperationExecutor; | ||
14 | |||
15 | /** | ||
16 | * @since 2.0 | ||
17 | * @noextend This class is not intended to be subclassed by clients. | ||
18 | */ | ||
19 | public abstract class SingleValueExtendOperationExecutor<T> extends ExtendOperationExecutor<T> { | ||
20 | protected int position; | ||
21 | |||
22 | /** | ||
23 | * @param position the frame position all values are to be added | ||
24 | */ | ||
25 | public SingleValueExtendOperationExecutor(int position) { | ||
26 | super(); | ||
27 | this.position = position; | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | protected final boolean fillInValue(T newValue, MatchingFrame frame, ISearchContext context) { | ||
32 | frame.setValue(position, newValue); | ||
33 | return true; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | protected final void cleanup(MatchingFrame frame, ISearchContext context) { | ||
38 | frame.setValue(position, null); | ||
39 | } | ||
40 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeCheck.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeCheck.java new file mode 100644 index 00000000..b9a10524 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeCheck.java | |||
@@ -0,0 +1,96 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.operations.generic; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.function.Function; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
18 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
19 | import tools.refinery.interpreter.localsearch.operations.CheckOperationExecutor; | ||
20 | import tools.refinery.interpreter.localsearch.operations.IIteratingSearchOperation; | ||
21 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
22 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
23 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
24 | import tools.refinery.interpreter.matchers.tuple.VolatileMaskedTuple; | ||
25 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
26 | |||
27 | /** | ||
28 | * @author Zoltan Ujhelyi | ||
29 | * @since 1.7 | ||
30 | * @noextend This class is not intended to be subclassed by clients. | ||
31 | */ | ||
32 | public class GenericTypeCheck implements ISearchOperation, IIteratingSearchOperation { | ||
33 | |||
34 | private class Executor extends CheckOperationExecutor { | ||
35 | private VolatileMaskedTuple maskedTuple; | ||
36 | |||
37 | private Executor() { | ||
38 | this.maskedTuple = new VolatileMaskedTuple(callMask); | ||
39 | } | ||
40 | |||
41 | @Override | ||
42 | protected boolean check(MatchingFrame frame, ISearchContext context) { | ||
43 | maskedTuple.updateTuple(frame); | ||
44 | return context.getRuntimeContext().containsTuple(type, maskedTuple); | ||
45 | } | ||
46 | |||
47 | @Override | ||
48 | public ISearchOperation getOperation() { | ||
49 | return GenericTypeCheck.this; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | private final IInputKey type; | ||
54 | private final List<Integer> positionList; | ||
55 | private final TupleMask callMask; | ||
56 | |||
57 | public GenericTypeCheck(IInputKey type, int[] positions, TupleMask callMask) { | ||
58 | this.callMask = callMask; | ||
59 | Preconditions.checkArgument(positions.length == type.getArity(), | ||
60 | "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(), | ||
61 | type.getArity(), positions.length); | ||
62 | List<Integer> modifiablePositionList = new ArrayList<>(); | ||
63 | for (int position : positions) { | ||
64 | modifiablePositionList.add(position); | ||
65 | } | ||
66 | this.positionList = Collections.unmodifiableList(modifiablePositionList); | ||
67 | this.type = type; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | public List<Integer> getVariablePositions() { | ||
72 | return positionList; | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public ISearchOperationExecutor createExecutor() { | ||
77 | return new Executor(); | ||
78 | } | ||
79 | |||
80 | @Override | ||
81 | public String toString() { | ||
82 | return toString(Object::toString); | ||
83 | } | ||
84 | |||
85 | @Override | ||
86 | public String toString(Function<Integer, String> variableMapping) { | ||
87 | return "check " + type.getPrettyPrintableName() + "(" | ||
88 | + positionList.stream().map(input -> String.format("+%s", variableMapping.apply(input))).collect(Collectors.joining(", ")) | ||
89 | + ")"; | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public IInputKey getIteratedInputKey() { | ||
94 | return type; | ||
95 | } | ||
96 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeExtend.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeExtend.java new file mode 100644 index 00000000..79db98aa --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeExtend.java | |||
@@ -0,0 +1,145 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.operations.generic; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.List; | ||
15 | import java.util.Objects; | ||
16 | import java.util.Set; | ||
17 | import java.util.function.Function; | ||
18 | import java.util.stream.Collectors; | ||
19 | |||
20 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
21 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
22 | import tools.refinery.interpreter.localsearch.operations.ExtendOperationExecutor; | ||
23 | import tools.refinery.interpreter.localsearch.operations.IIteratingSearchOperation; | ||
24 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
25 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
26 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
27 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
28 | import tools.refinery.interpreter.matchers.tuple.VolatileMaskedTuple; | ||
29 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
30 | |||
31 | /** | ||
32 | * @author Zoltan Ujhelyi | ||
33 | * @since 1.7 | ||
34 | * @noextend This class is not intended to be subclassed by clients. | ||
35 | */ | ||
36 | public class GenericTypeExtend implements IIteratingSearchOperation { | ||
37 | |||
38 | private class Executor extends ExtendOperationExecutor<Tuple> { | ||
39 | private final VolatileMaskedTuple maskedTuple; | ||
40 | |||
41 | public Executor() { | ||
42 | this.maskedTuple = new VolatileMaskedTuple(callMask); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) { | ||
47 | maskedTuple.updateTuple(frame); | ||
48 | return context.getRuntimeContext().enumerateTuples(type, indexerMask, maskedTuple).iterator(); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | protected boolean fillInValue(Tuple newTuple, MatchingFrame frame, ISearchContext context) { | ||
53 | for (Integer position : unboundVariableIndices) { | ||
54 | frame.setValue(position, null); | ||
55 | } | ||
56 | for (int i = 0; i < positions.length; i++) { | ||
57 | Object newValue = newTuple.get(i); | ||
58 | Object oldValue = frame.getValue(positions[i]); | ||
59 | if (oldValue != null && !Objects.equals(oldValue, newValue)) { | ||
60 | // If positions tuple maps more than one values for the same element (e.g. loop), it means that | ||
61 | // these arguments are to unified by the caller. In this case if the callee assigns different values | ||
62 | // the frame shall be considered a failed match | ||
63 | return false; | ||
64 | } | ||
65 | frame.setValue(positions[i], newValue); | ||
66 | } | ||
67 | return true; | ||
68 | } | ||
69 | |||
70 | @Override | ||
71 | protected void cleanup(MatchingFrame frame, ISearchContext context) { | ||
72 | for (Integer position : unboundVariableIndices) { | ||
73 | frame.setValue(position, null); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | @Override | ||
78 | public ISearchOperation getOperation() { | ||
79 | return GenericTypeExtend.this; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | private final IInputKey type; | ||
84 | private final int[] positions; | ||
85 | private final List<Integer> positionList; | ||
86 | private final Set<Integer> unboundVariableIndices; | ||
87 | private final TupleMask indexerMask; | ||
88 | private final TupleMask callMask; | ||
89 | |||
90 | /** | ||
91 | * | ||
92 | * @param type | ||
93 | * the type to execute the extend operation on | ||
94 | * @param positions | ||
95 | * the parameter positions that represent the variables of the input key | ||
96 | * @param unboundVariableIndices | ||
97 | * the set of positions that are bound at the start of the operation | ||
98 | */ | ||
99 | public GenericTypeExtend(IInputKey type, int[] positions, TupleMask callMask, TupleMask indexerMask, Set<Integer> unboundVariableIndices) { | ||
100 | Preconditions.checkArgument(positions.length == type.getArity(), | ||
101 | "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(), | ||
102 | type.getArity(), positions.length); | ||
103 | List<Integer> modifiablePositionList = new ArrayList<>(); | ||
104 | for (int position : positions) { | ||
105 | modifiablePositionList.add(position); | ||
106 | } | ||
107 | this.positionList = Collections.unmodifiableList(modifiablePositionList); | ||
108 | this.positions = positions; | ||
109 | this.type = type; | ||
110 | |||
111 | this.unboundVariableIndices = unboundVariableIndices; | ||
112 | this.indexerMask = indexerMask; | ||
113 | this.callMask = callMask; | ||
114 | } | ||
115 | |||
116 | @Override | ||
117 | public IInputKey getIteratedInputKey() { | ||
118 | return type; | ||
119 | } | ||
120 | |||
121 | @Override | ||
122 | public ISearchOperationExecutor createExecutor() { | ||
123 | return new Executor(); | ||
124 | } | ||
125 | |||
126 | @Override | ||
127 | public List<Integer> getVariablePositions() { | ||
128 | return positionList; | ||
129 | } | ||
130 | |||
131 | @Override | ||
132 | public String toString() { | ||
133 | return toString(Object::toString); | ||
134 | } | ||
135 | |||
136 | @Override | ||
137 | public String toString(Function<Integer, String> variableMapping) { | ||
138 | return "extend " + type.getPrettyPrintableName() + "(" | ||
139 | + positionList.stream() | ||
140 | .map(input -> String.format("%s%s", unboundVariableIndices.contains(input) ? "-" : "+", variableMapping.apply(input))) | ||
141 | .collect(Collectors.joining(", ")) | ||
142 | + ")"; | ||
143 | } | ||
144 | |||
145 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeExtendSingleValue.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeExtendSingleValue.java new file mode 100644 index 00000000..f381e00d --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/generic/GenericTypeExtendSingleValue.java | |||
@@ -0,0 +1,114 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.operations.generic; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.Iterator; | ||
14 | import java.util.List; | ||
15 | import java.util.Objects; | ||
16 | import java.util.function.Function; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
20 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
21 | import tools.refinery.interpreter.localsearch.operations.IIteratingSearchOperation; | ||
22 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
23 | import tools.refinery.interpreter.localsearch.operations.extend.SingleValueExtendOperationExecutor; | ||
24 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
25 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
26 | import tools.refinery.interpreter.matchers.tuple.VolatileMaskedTuple; | ||
27 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
28 | |||
29 | /** | ||
30 | * @author Zoltan Ujhelyi | ||
31 | * @since 1.7 | ||
32 | * @noextend This class is not intended to be subclassed by clients. | ||
33 | */ | ||
34 | public class GenericTypeExtendSingleValue implements IIteratingSearchOperation { | ||
35 | |||
36 | private class Executor extends SingleValueExtendOperationExecutor<Object> { | ||
37 | |||
38 | private final VolatileMaskedTuple maskedTuple; | ||
39 | |||
40 | public Executor(int position) { | ||
41 | super(position); | ||
42 | this.maskedTuple = new VolatileMaskedTuple(callMask); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | protected Iterator<? extends Object> getIterator(MatchingFrame frame, ISearchContext context) { | ||
47 | maskedTuple.updateTuple(frame); | ||
48 | return context.getRuntimeContext().enumerateValues(type, indexerMask, maskedTuple).iterator(); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public ISearchOperation getOperation() { | ||
53 | return GenericTypeExtendSingleValue.this; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | private final IInputKey type; | ||
58 | private final List<Integer> positionList; | ||
59 | private final TupleMask indexerMask; | ||
60 | private final TupleMask callMask; | ||
61 | private final int unboundVariableIndex; | ||
62 | |||
63 | /** | ||
64 | * | ||
65 | * @param type | ||
66 | * the type to execute the extend operation on | ||
67 | * @param positions | ||
68 | * the parameter positions that represent the variables of the input key | ||
69 | */ | ||
70 | public GenericTypeExtendSingleValue(IInputKey type, int[] positions, TupleMask callMask, TupleMask indexerMask, int unboundVariableIndex) { | ||
71 | Preconditions.checkArgument(positions.length == type.getArity(), | ||
72 | "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(), | ||
73 | type.getArity(), positions.length); | ||
74 | List<Integer> modifiablePositionList = new ArrayList<>(); | ||
75 | for (int position : positions) { | ||
76 | modifiablePositionList.add(position); | ||
77 | } | ||
78 | this.unboundVariableIndex = unboundVariableIndex; | ||
79 | this.positionList = Collections.unmodifiableList(modifiablePositionList); | ||
80 | this.type = type; | ||
81 | |||
82 | this.callMask = callMask; | ||
83 | this.indexerMask = indexerMask; | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public IInputKey getIteratedInputKey() { | ||
88 | return type; | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public ISearchOperationExecutor createExecutor() { | ||
93 | return new Executor(unboundVariableIndex); | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public List<Integer> getVariablePositions() { | ||
98 | return positionList; | ||
99 | } | ||
100 | |||
101 | @Override | ||
102 | public String toString() { | ||
103 | return toString(Object::toString); | ||
104 | } | ||
105 | |||
106 | @Override | ||
107 | public String toString(Function<Integer, String> variableMapping) { | ||
108 | return "extend " + type.getPrettyPrintableName() + "(" | ||
109 | + positionList.stream().map( | ||
110 | input -> String.format("%s%s", Objects.equals(input, unboundVariableIndex) ? "-" : "+", variableMapping.apply(input))) | ||
111 | .collect(Collectors.joining(", ")) | ||
112 | + ")"; | ||
113 | } | ||
114 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/util/CallInformation.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/util/CallInformation.java new file mode 100644 index 00000000..6241b33f --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/operations/util/CallInformation.java | |||
@@ -0,0 +1,186 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.operations.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | import java.util.function.Function; | ||
18 | import java.util.stream.Collectors; | ||
19 | |||
20 | import tools.refinery.interpreter.localsearch.matcher.CallWithAdornment; | ||
21 | import tools.refinery.interpreter.localsearch.matcher.MatcherReference; | ||
22 | import tools.refinery.interpreter.matchers.psystem.IQueryReference; | ||
23 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
24 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.PatternCallBasedDeferred; | ||
25 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure; | ||
26 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
27 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
28 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
29 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
30 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
31 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
32 | |||
33 | /** | ||
34 | * This class stores a precompiled version of call-related metadata and masks for local search operations | ||
35 | * | ||
36 | * @author Zoltan Ujhelyi | ||
37 | * @since 1.7 | ||
38 | */ | ||
39 | public final class CallInformation { | ||
40 | |||
41 | private final TupleMask fullFrameMask; | ||
42 | private final TupleMask thinFrameMask; | ||
43 | private final TupleMask parameterMask; | ||
44 | private final int[] freeParameterIndices; | ||
45 | |||
46 | private final Map<PParameter, Integer> mapping = new HashMap<>(); | ||
47 | private final Set<PParameter> adornment = new HashSet<>(); | ||
48 | private final PQuery referredQuery; | ||
49 | private final MatcherReference matcherReference; | ||
50 | private final IQueryReference call; | ||
51 | private CallWithAdornment callWithAdornment; | ||
52 | |||
53 | public static CallInformation create(PatternCallBasedDeferred constraint, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) { | ||
54 | return new CallInformation(constraint.getActualParametersTuple(), constraint, bindings, variableMapping); | ||
55 | } | ||
56 | |||
57 | public static CallInformation create(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) { | ||
58 | return new CallInformation(pCall.getVariablesTuple(), pCall, bindings, variableMapping); | ||
59 | } | ||
60 | |||
61 | public static CallInformation create(BinaryTransitiveClosure constraint, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) { | ||
62 | return new CallInformation(constraint.getVariablesTuple(), constraint, bindings, variableMapping); | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * @since 2.0 | ||
67 | */ | ||
68 | public static CallInformation create(BinaryReflexiveTransitiveClosure constraint, Map<PVariable, Integer> variableMapping, Set<Integer> bindings) { | ||
69 | return new CallInformation(constraint.getVariablesTuple(), constraint, bindings, variableMapping); | ||
70 | } | ||
71 | |||
72 | private CallInformation(Tuple actualParameters, IQueryReference call, final Set<Integer> bindings, | ||
73 | Map<PVariable, Integer> variableMapping) { | ||
74 | this.call = call; | ||
75 | this.referredQuery = call.getReferredQuery(); | ||
76 | int keySize = actualParameters.getSize(); | ||
77 | List<Integer> parameterMaskIndices = new ArrayList<>(); | ||
78 | int[] fullParameterMaskIndices = new int[keySize]; | ||
79 | for (int i = 0; i < keySize; i++) { | ||
80 | PParameter symbolicParameter = referredQuery.getParameters().get(i); | ||
81 | PVariable parameter = (PVariable) actualParameters.get(i); | ||
82 | Integer originalFrameIndex = variableMapping.get(parameter); | ||
83 | mapping.put(symbolicParameter, originalFrameIndex); | ||
84 | fullParameterMaskIndices[i] = originalFrameIndex; | ||
85 | if (bindings.contains(originalFrameIndex)) { | ||
86 | parameterMaskIndices.add(originalFrameIndex); | ||
87 | adornment.add(symbolicParameter); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | thinFrameMask = TupleMask.fromSelectedIndices(variableMapping.size(), parameterMaskIndices); | ||
92 | fullFrameMask = TupleMask.fromSelectedIndices(variableMapping.size(), fullParameterMaskIndices); | ||
93 | |||
94 | // This second iteration is necessary as we don't know beforehand the number of bound parameters | ||
95 | int[] boundParameterIndices = new int[adornment.size()]; | ||
96 | int boundIndex = 0; | ||
97 | freeParameterIndices = new int[keySize - adornment.size()]; | ||
98 | int freeIndex = 0; | ||
99 | for (int i = 0; i < keySize; i++) { | ||
100 | if (bindings.contains(variableMapping.get(actualParameters.get(i)))) { | ||
101 | boundParameterIndices[boundIndex] = i; | ||
102 | boundIndex++; | ||
103 | } else { | ||
104 | freeParameterIndices[freeIndex] = i; | ||
105 | freeIndex++; | ||
106 | } | ||
107 | } | ||
108 | parameterMask = TupleMask.fromSelectedIndices(keySize, boundParameterIndices); | ||
109 | callWithAdornment = new CallWithAdornment(call, adornment); | ||
110 | matcherReference = callWithAdornment.getMatcherReference(); | ||
111 | } | ||
112 | |||
113 | /** | ||
114 | * Returns a mask describing how the bound variables of a Matching Frame are mapped to parameter indexes | ||
115 | */ | ||
116 | public TupleMask getThinFrameMask() { | ||
117 | return thinFrameMask; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * Returns a mask describing how all variables of a Matching Frame are mapped to parameter indexes | ||
122 | */ | ||
123 | public TupleMask getFullFrameMask() { | ||
124 | return fullFrameMask; | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * Returns a mask describing the adornment the called pattern uses | ||
129 | */ | ||
130 | public TupleMask getParameterMask() { | ||
131 | return parameterMask; | ||
132 | } | ||
133 | |||
134 | public MatcherReference getReference() { | ||
135 | return matcherReference; | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * @since 2.1 | ||
140 | */ | ||
141 | public IQueryReference getCall() { | ||
142 | return call; | ||
143 | } | ||
144 | |||
145 | /** | ||
146 | * @since 2.1 | ||
147 | */ | ||
148 | public CallWithAdornment getCallWithAdornment() { | ||
149 | return callWithAdornment; | ||
150 | } | ||
151 | |||
152 | /** | ||
153 | * Returns the parameter indices that are unbound before the call | ||
154 | */ | ||
155 | public int[] getFreeParameterIndices() { | ||
156 | return freeParameterIndices; | ||
157 | } | ||
158 | |||
159 | public List<Integer> getVariablePositions() { | ||
160 | List<Integer> variables = new ArrayList<>(mapping.size()); | ||
161 | for(PParameter p : referredQuery.getParameters()){ | ||
162 | variables.add(mapping.get(p)); | ||
163 | } | ||
164 | return variables; | ||
165 | } | ||
166 | |||
167 | |||
168 | |||
169 | @Override | ||
170 | public String toString() { | ||
171 | return toString(Object::toString); | ||
172 | } | ||
173 | |||
174 | /** | ||
175 | * @since 2.0 | ||
176 | */ | ||
177 | public String toString(Function<Integer, String> variableMapping) { | ||
178 | return referredQuery.getFullyQualifiedName() + "(" | ||
179 | + referredQuery.getParameters().stream().map( | ||
180 | input -> (adornment.contains(input) ? "+" : "-") + variableMapping.apply(mapping.get(input))) | ||
181 | .collect(Collectors.joining(",")) | ||
182 | + ")"; | ||
183 | } | ||
184 | |||
185 | |||
186 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/IPlanDescriptor.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/IPlanDescriptor.java new file mode 100644 index 00000000..a855b0b0 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/IPlanDescriptor.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.plan; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
15 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
16 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
17 | |||
18 | /** | ||
19 | * Denotes an executable plan | ||
20 | * | ||
21 | * @author Grill Balázs | ||
22 | * @since 1.4 | ||
23 | * | ||
24 | */ | ||
25 | public interface IPlanDescriptor { | ||
26 | |||
27 | /** | ||
28 | * The query which this plan implements | ||
29 | */ | ||
30 | public PQuery getQuery(); | ||
31 | |||
32 | /** | ||
33 | * The executable search plans for each body in the query | ||
34 | * @since 2.0 | ||
35 | */ | ||
36 | public Collection<SearchPlanForBody> getPlan(); | ||
37 | |||
38 | /** | ||
39 | * The set of parameters this plan assumes to be bound | ||
40 | */ | ||
41 | public Set<PParameter> getAdornment(); | ||
42 | |||
43 | /** | ||
44 | * The collection of {@link IInputKey}s which needs to be iterated during the execution of this plan. For optimal | ||
45 | * performance, instances of these keys might be indexed. | ||
46 | */ | ||
47 | public Set<IInputKey> getIteratedKeys(); | ||
48 | |||
49 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/IPlanProvider.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/IPlanProvider.java new file mode 100644 index 00000000..d7112bcc --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/IPlanProvider.java | |||
@@ -0,0 +1,33 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.plan; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.matcher.MatcherReference; | ||
12 | import tools.refinery.interpreter.localsearch.matcher.integration.LocalSearchHints; | ||
13 | import tools.refinery.interpreter.localsearch.planner.compiler.IOperationCompiler; | ||
14 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
15 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
16 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
17 | |||
18 | /** | ||
19 | * @author Grill Balázs | ||
20 | * @since 1.4 | ||
21 | * @noreference This interface is not intended to be referenced by clients. | ||
22 | */ | ||
23 | public interface IPlanProvider { | ||
24 | |||
25 | /** | ||
26 | * @throws InterpreterRuntimeException | ||
27 | * @since 2.1 | ||
28 | */ | ||
29 | public IPlanDescriptor getPlan(IQueryBackendContext backend, IOperationCompiler compiler, | ||
30 | ResultProviderRequestor resultProviderRequestor, | ||
31 | LocalSearchHints configuration, MatcherReference key); | ||
32 | |||
33 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/PlanDescriptor.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/PlanDescriptor.java new file mode 100644 index 00000000..f4196032 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/PlanDescriptor.java | |||
@@ -0,0 +1,84 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.plan; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import tools.refinery.interpreter.localsearch.operations.IIteratingSearchOperation; | ||
20 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
21 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
22 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
23 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
24 | |||
25 | /** | ||
26 | * @author Grill Balázs | ||
27 | * @since 1.4 | ||
28 | * | ||
29 | */ | ||
30 | public class PlanDescriptor implements IPlanDescriptor { | ||
31 | |||
32 | private final PQuery pquery; | ||
33 | private final List<SearchPlanForBody> plan; | ||
34 | private final Set<PParameter> adornment; | ||
35 | private Set<IInputKey> iteratedKeys = null; | ||
36 | |||
37 | public PlanDescriptor(PQuery pquery, Collection<SearchPlanForBody> plan, Set<PParameter> adornment) { | ||
38 | this.pquery = pquery; | ||
39 | this.plan = new ArrayList<>(plan); | ||
40 | this.adornment = adornment; | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | public PQuery getQuery() { | ||
45 | return pquery; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Collection<SearchPlanForBody> getPlan() { | ||
50 | return plan; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public Set<PParameter> getAdornment() { | ||
55 | return adornment; | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public Set<IInputKey> getIteratedKeys() { | ||
60 | if (iteratedKeys == null){ | ||
61 | Set<IInputKey> keys = new HashSet<>(); | ||
62 | for(SearchPlanForBody bodyPlan : plan){ | ||
63 | for(ISearchOperation operation : bodyPlan.getCompiledOperations()){ | ||
64 | if (operation instanceof IIteratingSearchOperation){ | ||
65 | keys.add(((IIteratingSearchOperation) operation).getIteratedInputKey()); | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | iteratedKeys = Collections.unmodifiableSet(keys); | ||
70 | } | ||
71 | return iteratedKeys; | ||
72 | } | ||
73 | |||
74 | @Override | ||
75 | public String toString() { | ||
76 | return new StringBuilder().append("Plan for ").append(pquery.getFullyQualifiedName()).append("(") | ||
77 | .append(adornment.stream().map(PParameter::getName).collect(Collectors.joining(","))) | ||
78 | .append("{") | ||
79 | .append(plan.stream().map(Object::toString).collect(Collectors.joining("}\n{"))) | ||
80 | .append("}") | ||
81 | .toString(); | ||
82 | } | ||
83 | |||
84 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlan.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlan.java new file mode 100644 index 00000000..1cbdd92d --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlan.java | |||
@@ -0,0 +1,99 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Zoltan Ujhelyi, 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.interpreter.localsearch.plan; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.Map.Entry; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
19 | import tools.refinery.interpreter.matchers.psystem.PBody; | ||
20 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
21 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
22 | |||
23 | /** | ||
24 | * A SearchPlan stores a collection of SearchPlanOperations for a fixed order of variables. | ||
25 | * | ||
26 | * @author Zoltan Ujhelyi | ||
27 | * | ||
28 | */ | ||
29 | public class SearchPlan { | ||
30 | |||
31 | private final List<ISearchOperation> operations; | ||
32 | private final Map<Integer, PVariable> variableMapping; | ||
33 | private final TupleMask parameterMask; | ||
34 | private final PBody body; | ||
35 | |||
36 | /** | ||
37 | * @since 2.0 | ||
38 | */ | ||
39 | public SearchPlan(PBody body, List<ISearchOperation> operations, TupleMask parameterMask, Map<PVariable, Integer> variableMapping) { | ||
40 | this.body = body; | ||
41 | this.operations = Collections.unmodifiableList(new ArrayList<>(operations)); | ||
42 | this.parameterMask = parameterMask; | ||
43 | this.variableMapping = Collections.unmodifiableMap(variableMapping.entrySet().stream() | ||
44 | .collect(Collectors.toMap(Entry::getValue, Entry::getKey))); | ||
45 | } | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Returns an immutable list of operations stored in the plan. | ||
50 | * @return the operations | ||
51 | */ | ||
52 | public List<ISearchOperation> getOperations() { | ||
53 | return operations; | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * Returns an immutable map of variable mappings for the plan | ||
58 | * @since 2.0 | ||
59 | */ | ||
60 | public Map<Integer, PVariable> getVariableMapping() { | ||
61 | return variableMapping; | ||
62 | } | ||
63 | |||
64 | /** | ||
65 | * Returns the index of a given operation in the plan | ||
66 | * @since 2.0 | ||
67 | */ | ||
68 | public int getOperationIndex(ISearchOperation operation) { | ||
69 | return operations.indexOf(operation); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * @since 2.0 | ||
74 | */ | ||
75 | public TupleMask getParameterMask() { | ||
76 | return parameterMask; | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * @since 2.0 | ||
81 | */ | ||
82 | public PBody getSourceBody() { | ||
83 | return body; | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public String toString() { | ||
88 | StringBuilder sb = new StringBuilder(); | ||
89 | |||
90 | sb.append("{\n"); | ||
91 | for(ISearchOperation operation : this.getOperations()){ | ||
92 | sb.append("\t"); | ||
93 | sb.append(operation); | ||
94 | sb.append("\n"); | ||
95 | } | ||
96 | sb.append("}"); | ||
97 | return sb.toString(); | ||
98 | } | ||
99 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlanExecutor.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlanExecutor.java new file mode 100644 index 00000000..60c2e023 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlanExecutor.java | |||
@@ -0,0 +1,209 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2004-2008 Akos Horvath, Gergely Varro Zoltan Ujhelyi 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 | |||
10 | package tools.refinery.interpreter.localsearch.plan; | ||
11 | |||
12 | |||
13 | import java.util.Collections; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.concurrent.CopyOnWriteArrayList; | ||
17 | import java.util.stream.Collectors; | ||
18 | |||
19 | import org.apache.log4j.Logger; | ||
20 | import tools.refinery.interpreter.localsearch.exceptions.LocalSearchException; | ||
21 | import tools.refinery.interpreter.localsearch.matcher.ILocalSearchAdaptable; | ||
22 | import tools.refinery.interpreter.localsearch.matcher.ILocalSearchAdapter; | ||
23 | import tools.refinery.interpreter.localsearch.matcher.ISearchContext; | ||
24 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
25 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
26 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
27 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
28 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
29 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
30 | |||
31 | /** | ||
32 | * A search plan executor is used to execute {@link SearchPlan} instances. | ||
33 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
34 | */ | ||
35 | public class SearchPlanExecutor implements ILocalSearchAdaptable { | ||
36 | |||
37 | private int currentOperation; | ||
38 | private final List<ISearchOperation.ISearchOperationExecutor> operations; | ||
39 | private final SearchPlan plan; | ||
40 | private final ISearchContext context; | ||
41 | private final List<ILocalSearchAdapter> adapters = new CopyOnWriteArrayList<>(); | ||
42 | |||
43 | /** | ||
44 | * @since 2.0 | ||
45 | */ | ||
46 | public Map<Integer, PVariable> getVariableMapping() { | ||
47 | return plan.getVariableMapping(); | ||
48 | } | ||
49 | |||
50 | public int getCurrentOperation() { | ||
51 | return currentOperation; | ||
52 | } | ||
53 | |||
54 | public SearchPlan getSearchPlan() { | ||
55 | return plan; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * @since 1.7 | ||
60 | */ | ||
61 | public TupleMask getParameterMask() { | ||
62 | return plan.getParameterMask(); | ||
63 | } | ||
64 | |||
65 | @Override | ||
66 | public void addAdapters(List<ILocalSearchAdapter> adapters) { | ||
67 | for(ILocalSearchAdapter adapter : adapters){ | ||
68 | if (!this.adapters.contains(adapter)){ | ||
69 | this.adapters.add(adapter); | ||
70 | adapter.adapterRegistered(this); | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | @Override | ||
76 | public void removeAdapters(List<ILocalSearchAdapter> adapters) { | ||
77 | for (ILocalSearchAdapter adapter : adapters) { | ||
78 | if (this.adapters.remove(adapter)){ | ||
79 | adapter.adapterUnregistered(this); | ||
80 | } | ||
81 | } | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * @since 2.0 | ||
86 | */ | ||
87 | public SearchPlanExecutor(SearchPlan plan, ISearchContext context) { | ||
88 | Preconditions.checkArgument(context != null, "Context cannot be null"); | ||
89 | this.plan = plan; | ||
90 | this.context = context; | ||
91 | operations = plan.getOperations().stream().map(ISearchOperation::createExecutor).collect(Collectors.toList()); | ||
92 | this.currentOperation = -1; | ||
93 | } | ||
94 | |||
95 | |||
96 | private void init(MatchingFrame frame) { | ||
97 | if (currentOperation == -1) { | ||
98 | currentOperation++; | ||
99 | ISearchOperation.ISearchOperationExecutor operation = operations.get(currentOperation); | ||
100 | if (!adapters.isEmpty()){ | ||
101 | for (ILocalSearchAdapter adapter : adapters) { | ||
102 | adapter.executorInitializing(plan, frame); | ||
103 | } | ||
104 | } | ||
105 | operation.onInitialize(frame, context); | ||
106 | } else if (currentOperation == operations.size()) { | ||
107 | currentOperation--; | ||
108 | } else { | ||
109 | throw new LocalSearchException(LocalSearchException.PLAN_EXECUTION_ERROR); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | |||
114 | /** | ||
115 | * Calculates the cost of the search plan. | ||
116 | */ | ||
117 | public double cost() { | ||
118 | /* default generated stub */ | ||
119 | return 0.0; | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * @throws InterpreterRuntimeException | ||
124 | */ | ||
125 | public boolean execute(MatchingFrame frame) { | ||
126 | int upperBound = operations.size() - 1; | ||
127 | init(frame); | ||
128 | operationSelected(frame, currentOperation, false); | ||
129 | while (currentOperation >= 0 && currentOperation <= upperBound) { | ||
130 | if (operations.get(currentOperation).execute(frame, context)) { | ||
131 | operationExecuted(frame, currentOperation, true); | ||
132 | currentOperation++; | ||
133 | operationSelected(frame, currentOperation, false); | ||
134 | if (currentOperation <= upperBound) { | ||
135 | ISearchOperation.ISearchOperationExecutor operation = operations.get(currentOperation); | ||
136 | operation.onInitialize(frame, context); | ||
137 | } | ||
138 | } else { | ||
139 | operationExecuted(frame, currentOperation, false); | ||
140 | ISearchOperation.ISearchOperationExecutor operation = operations.get(currentOperation); | ||
141 | operation.onBacktrack(frame, context); | ||
142 | currentOperation--; | ||
143 | operationSelected(frame, currentOperation, true); | ||
144 | } | ||
145 | } | ||
146 | boolean matchFound = currentOperation > upperBound; | ||
147 | if (matchFound && !adapters.isEmpty()) { | ||
148 | for (ILocalSearchAdapter adapter : adapters) { | ||
149 | adapter.matchFound(plan, frame); | ||
150 | } | ||
151 | } | ||
152 | return matchFound; | ||
153 | } | ||
154 | |||
155 | public void resetPlan() { | ||
156 | currentOperation = -1; | ||
157 | } | ||
158 | |||
159 | public void printDebugInformation() { | ||
160 | for (int i = 0; i < operations.size(); i++) { | ||
161 | Logger.getRootLogger().debug("[" + i + "]\t" + operations.get(i).toString()); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | private void operationExecuted(MatchingFrame frame, int operationIndex, boolean isSuccessful) { | ||
166 | if (!adapters.isEmpty()){ | ||
167 | for (ILocalSearchAdapter adapter : adapters) { | ||
168 | adapter.operationExecuted(plan, operations.get(operationIndex).getOperation(), frame, isSuccessful); | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
173 | private void operationSelected(MatchingFrame frame, int operationIndex, boolean isBacktrack) { | ||
174 | if (!adapters.isEmpty() && operationIndex >= 0 && operationIndex < operations.size()){ | ||
175 | for (ILocalSearchAdapter adapter : adapters) { | ||
176 | adapter.operationSelected(plan, operations.get(operationIndex).getOperation(), frame, isBacktrack); | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | |||
181 | public ISearchContext getContext() { | ||
182 | return context; | ||
183 | } | ||
184 | |||
185 | @Override | ||
186 | public List<ILocalSearchAdapter> getAdapters() { | ||
187 | return Collections.<ILocalSearchAdapter>unmodifiableList(this.adapters); | ||
188 | } | ||
189 | |||
190 | @Override | ||
191 | public void addAdapter(ILocalSearchAdapter adapter) { | ||
192 | addAdapters(Collections.singletonList(adapter)); | ||
193 | } | ||
194 | |||
195 | @Override | ||
196 | public void removeAdapter(ILocalSearchAdapter adapter) { | ||
197 | removeAdapters(Collections.singletonList(adapter)); | ||
198 | } | ||
199 | |||
200 | @Override | ||
201 | public String toString() { | ||
202 | if (operations == null) { | ||
203 | return "Unspecified plan"; | ||
204 | } else { | ||
205 | return operations.stream().map(Object::toString).collect(Collectors.joining("\n")); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlanForBody.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlanForBody.java new file mode 100644 index 00000000..6910df26 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SearchPlanForBody.java | |||
@@ -0,0 +1,115 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.plan; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Arrays; | ||
13 | import java.util.Collection; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.stream.Collectors; | ||
17 | |||
18 | import tools.refinery.interpreter.localsearch.matcher.CallWithAdornment; | ||
19 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
20 | import tools.refinery.interpreter.matchers.planning.SubPlan; | ||
21 | import tools.refinery.interpreter.matchers.psystem.PBody; | ||
22 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
23 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
24 | |||
25 | /** | ||
26 | * This class is responsible for storing the results of the planner and operation compiler for a selected body. | ||
27 | * @since 2.0 | ||
28 | * @noinstantiate This class is not intended to be instantiated by clients. | ||
29 | */ | ||
30 | public class SearchPlanForBody { | ||
31 | |||
32 | private final PBody body; | ||
33 | private final Map<PVariable, Integer> variableKeys; | ||
34 | private final int[] parameterKeys; | ||
35 | private final SubPlan plan; | ||
36 | private final List<ISearchOperation> compiledOperations; | ||
37 | private final Collection<CallWithAdornment> dependencies; | ||
38 | private final double cost; | ||
39 | private final Object internalRepresentation; | ||
40 | |||
41 | /** | ||
42 | * @since 2.1 | ||
43 | */ | ||
44 | public SearchPlanForBody(PBody body, Map<PVariable, Integer> variableKeys, | ||
45 | SubPlan plan, List<ISearchOperation> compiledOperations, Collection<CallWithAdornment> dependencies, | ||
46 | Object internalRepresentation, double cost) { | ||
47 | super(); | ||
48 | this.body = body; | ||
49 | this.variableKeys = variableKeys; | ||
50 | this.plan = plan; | ||
51 | this.internalRepresentation = internalRepresentation; | ||
52 | this.cost = cost; | ||
53 | List<PVariable> parameters = body.getSymbolicParameterVariables(); | ||
54 | parameterKeys = new int[parameters.size()]; | ||
55 | for (int i=0; i<parameters.size(); i++) { | ||
56 | parameterKeys[i] = variableKeys.get(parameters.get(i)); | ||
57 | } | ||
58 | this.compiledOperations = new ArrayList<>(compiledOperations.size()+1); | ||
59 | this.compiledOperations.addAll(compiledOperations); | ||
60 | |||
61 | this.dependencies = new ArrayList<>(dependencies); | ||
62 | } | ||
63 | |||
64 | public PBody getBody() { | ||
65 | return body; | ||
66 | } | ||
67 | |||
68 | public Map<PVariable, Integer> getVariableKeys() { | ||
69 | return variableKeys; | ||
70 | } | ||
71 | |||
72 | public int[] getParameterKeys() { | ||
73 | return Arrays.copyOf(parameterKeys, parameterKeys.length); | ||
74 | } | ||
75 | |||
76 | public List<ISearchOperation> getCompiledOperations() { | ||
77 | return compiledOperations; | ||
78 | } | ||
79 | |||
80 | public SubPlan getPlan() { | ||
81 | return plan; | ||
82 | } | ||
83 | |||
84 | public Collection<CallWithAdornment> getDependencies() { | ||
85 | return dependencies; | ||
86 | } | ||
87 | |||
88 | public TupleMask calculateParameterMask() { | ||
89 | return TupleMask.fromSelectedIndices(variableKeys.size(), parameterKeys); | ||
90 | } | ||
91 | |||
92 | @Override | ||
93 | public String toString() { | ||
94 | return compiledOperations.stream().map(Object::toString).collect(Collectors.joining("\n")); | ||
95 | } | ||
96 | |||
97 | /** | ||
98 | * @since 2.1 | ||
99 | */ | ||
100 | public double getCost() { | ||
101 | return cost; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * @return The internal representation of the search plan, if any, for traceability | ||
106 | * @since 2.1 | ||
107 | */ | ||
108 | public Object getInternalRepresentation() { | ||
109 | return internalRepresentation; | ||
110 | } | ||
111 | |||
112 | |||
113 | |||
114 | |||
115 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SimplePlanProvider.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SimplePlanProvider.java new file mode 100644 index 00000000..ae6beae9 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/plan/SimplePlanProvider.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.plan; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | |||
13 | import org.apache.log4j.Logger; | ||
14 | import tools.refinery.interpreter.localsearch.matcher.MatcherReference; | ||
15 | import tools.refinery.interpreter.localsearch.matcher.integration.LocalSearchHints; | ||
16 | import tools.refinery.interpreter.localsearch.planner.LocalSearchPlanner; | ||
17 | import tools.refinery.interpreter.localsearch.planner.compiler.IOperationCompiler; | ||
18 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
19 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
20 | |||
21 | /** | ||
22 | * A plan provider implementation which caches previously calculated plans to avoid re-planning for the same adornment | ||
23 | * | ||
24 | * @author Grill Balázs | ||
25 | * @since 1.7 | ||
26 | * | ||
27 | */ | ||
28 | public class SimplePlanProvider implements IPlanProvider { | ||
29 | |||
30 | private final Logger logger; | ||
31 | |||
32 | public SimplePlanProvider(Logger logger) { | ||
33 | this.logger = logger; | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | public IPlanDescriptor getPlan(IQueryBackendContext backend, IOperationCompiler compiler, | ||
38 | final ResultProviderRequestor resultRequestor, | ||
39 | final LocalSearchHints configuration, MatcherReference key) { | ||
40 | |||
41 | LocalSearchPlanner planner = new LocalSearchPlanner(backend, compiler, logger, configuration, resultRequestor); | ||
42 | |||
43 | Collection<SearchPlanForBody> plansForBodies = planner.plan(key.getQuery(), key.getAdornment()); | ||
44 | |||
45 | IPlanDescriptor plan = new PlanDescriptor(key.getQuery(), plansForBodies, key.getAdornment()); | ||
46 | return plan; | ||
47 | } | ||
48 | |||
49 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/ILocalSearchPlanner.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/ILocalSearchPlanner.java new file mode 100644 index 00000000..a50eed8c --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/ILocalSearchPlanner.java | |||
@@ -0,0 +1,38 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.planner; | ||
10 | |||
11 | import java.util.Collection; | ||
12 | import java.util.Set; | ||
13 | |||
14 | import tools.refinery.interpreter.localsearch.plan.SearchPlanForBody; | ||
15 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
16 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
17 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
18 | |||
19 | /** | ||
20 | * @author Zoltan Ujhelyi | ||
21 | * @since 1.7 | ||
22 | */ | ||
23 | public interface ILocalSearchPlanner { | ||
24 | |||
25 | /** | ||
26 | * Creates executable plans for the provided query. It is required to call one of the | ||
27 | * <code>initializePlanner()</code> methods before calling this method. | ||
28 | * | ||
29 | * @param querySpec | ||
30 | * @param boundParameters | ||
31 | * a set of bound parameters | ||
32 | * @return a mapping between ISearchOperation list and a mapping, that holds a PVariable-Integer mapping for the | ||
33 | * list of ISearchOperations | ||
34 | * @throws InterpreterRuntimeException | ||
35 | */ | ||
36 | Collection<SearchPlanForBody> plan(PQuery querySpec, Set<PParameter> boundParameters); | ||
37 | |||
38 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/ISearchPlanCodeGenerator.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/ISearchPlanCodeGenerator.java new file mode 100644 index 00000000..0c2f85c7 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/ISearchPlanCodeGenerator.java | |||
@@ -0,0 +1,23 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.planner; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
14 | |||
15 | /** | ||
16 | * @author Marton Bur | ||
17 | * | ||
18 | */ | ||
19 | public interface ISearchPlanCodeGenerator { | ||
20 | |||
21 | void compile(List<List<ISearchOperation>> plans); | ||
22 | |||
23 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/LocalSearchPlanner.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/LocalSearchPlanner.java new file mode 100644 index 00000000..38c64d4c --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/LocalSearchPlanner.java | |||
@@ -0,0 +1,140 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.planner; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import org.apache.log4j.Logger; | ||
20 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
21 | import tools.refinery.interpreter.localsearch.planner.compiler.IOperationCompiler; | ||
22 | import tools.refinery.interpreter.localsearch.matcher.integration.LocalSearchHints; | ||
23 | import tools.refinery.interpreter.localsearch.plan.SearchPlanForBody; | ||
24 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
25 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
26 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
27 | import tools.refinery.interpreter.matchers.planning.SubPlan; | ||
28 | import tools.refinery.interpreter.matchers.psystem.PBody; | ||
29 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
30 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.ExportedParameter; | ||
31 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
32 | import tools.refinery.interpreter.matchers.psystem.queries.PQuery; | ||
33 | import tools.refinery.interpreter.matchers.psystem.rewriters.PBodyNormalizer; | ||
34 | import tools.refinery.interpreter.matchers.psystem.rewriters.PDisjunctionRewriter; | ||
35 | import tools.refinery.interpreter.matchers.psystem.rewriters.PDisjunctionRewriterCacher; | ||
36 | import tools.refinery.interpreter.matchers.psystem.rewriters.PQueryFlattener; | ||
37 | |||
38 | /** | ||
39 | * | ||
40 | * @author Marton Bur | ||
41 | * @noreference This class is not intended to be referenced by clients. | ||
42 | */ | ||
43 | public class LocalSearchPlanner implements ILocalSearchPlanner { | ||
44 | |||
45 | // Externally set tools for planning | ||
46 | private final PDisjunctionRewriter preprocessor; | ||
47 | private final LocalSearchRuntimeBasedStrategy plannerStrategy; | ||
48 | private final IQueryRuntimeContext runtimeContext; | ||
49 | private final LocalSearchHints configuration; | ||
50 | private final IOperationCompiler operationCompiler; | ||
51 | private final IQueryBackendContext context; | ||
52 | private final ResultProviderRequestor resultRequestor; | ||
53 | |||
54 | /** | ||
55 | * @param resultRequestor | ||
56 | * @since 1.7 | ||
57 | */ | ||
58 | public LocalSearchPlanner(IQueryBackendContext backendContext, IOperationCompiler compiler, Logger logger, | ||
59 | final LocalSearchHints configuration, ResultProviderRequestor resultRequestor) | ||
60 | { | ||
61 | |||
62 | this.runtimeContext = backendContext.getRuntimeContext(); | ||
63 | this.configuration = configuration; | ||
64 | this.operationCompiler = compiler; | ||
65 | this.resultRequestor = resultRequestor; | ||
66 | PQueryFlattener flattener = new PQueryFlattener(configuration.getFlattenCallPredicate()); | ||
67 | /* | ||
68 | * TODO https://bugs.eclipse.org/bugs/show_bug.cgi?id=439358: The normalizer is initialized with the false | ||
69 | * parameter to turn off unary constraint elimination to work around an issue related to plan ordering: the | ||
70 | * current implementation of the feature target checking operations expect that the source types were checked | ||
71 | * before. However, this causes duplicate constraint checks in the search plan that might affect performance | ||
72 | * negatively. | ||
73 | */ | ||
74 | PBodyNormalizer normalizer = new PBodyNormalizer(runtimeContext.getMetaContext()) { | ||
75 | |||
76 | @Override | ||
77 | protected boolean shouldCalculateImpliedTypes(PQuery query) { | ||
78 | return false; | ||
79 | } | ||
80 | }; | ||
81 | preprocessor = new PDisjunctionRewriterCacher(flattener, normalizer); | ||
82 | |||
83 | plannerStrategy = new LocalSearchRuntimeBasedStrategy(); | ||
84 | |||
85 | context = backendContext; | ||
86 | } | ||
87 | |||
88 | /** | ||
89 | * Creates executable plans for the provided query. It is required to call one of the | ||
90 | * <code>initializePlanner()</code> methods before calling this method. | ||
91 | * | ||
92 | * @param querySpec | ||
93 | * @param boundParameters | ||
94 | * a set of bound parameters | ||
95 | * @return a mapping between ISearchOperation list and a mapping, that holds a PVariable-Integer mapping for the | ||
96 | * list of ISearchOperations | ||
97 | */ | ||
98 | @Override | ||
99 | public Collection<SearchPlanForBody> plan(PQuery querySpec, Set<PParameter> boundParameters) { | ||
100 | // 1. Preparation | ||
101 | preprocessor.setTraceCollector(configuration.getTraceCollector()); | ||
102 | Set<PBody> normalizedBodies = preprocessor.rewrite(querySpec.getDisjunctBodies()).getBodies(); | ||
103 | |||
104 | List<SearchPlanForBody> plansForBodies = new ArrayList<>(normalizedBodies.size()); | ||
105 | |||
106 | for (PBody normalizedBody : normalizedBodies) { | ||
107 | // 2. Plan creation | ||
108 | // Context has matchers for the referred Queries (IQuerySpecifications) | ||
109 | Set<PVariable> boundVariables = calculatePatternAdornmentForPlanner(boundParameters, normalizedBody); | ||
110 | PlanState searchPlanInternal = plannerStrategy.plan(normalizedBody, boundVariables, context, resultRequestor, configuration); | ||
111 | SubPlan plan = plannerStrategy.convertPlan(boundVariables, searchPlanInternal); | ||
112 | // 3. PConstraint -> POperation compilation step | ||
113 | // * Pay extra caution to extend operations, when more than one variables are unbound | ||
114 | List<ISearchOperation> compiledOperations = operationCompiler.compile(plan, boundParameters); | ||
115 | // Store the variable mappings for the plans for debug purposes (traceability information) | ||
116 | SearchPlanForBody compiledPlan = new SearchPlanForBody(normalizedBody, | ||
117 | operationCompiler.getVariableMappings(), plan, compiledOperations, | ||
118 | operationCompiler.getDependencies(), | ||
119 | searchPlanInternal, searchPlanInternal.getCost()); | ||
120 | |||
121 | plansForBodies.add(compiledPlan); | ||
122 | } | ||
123 | |||
124 | return plansForBodies; | ||
125 | } | ||
126 | |||
127 | private Set<PVariable> calculatePatternAdornmentForPlanner(Set<PParameter> boundParameters, PBody normalizedBody) { | ||
128 | Map<PParameter, PVariable> parameterMapping = new HashMap<>(); | ||
129 | for (ExportedParameter constraint : normalizedBody.getSymbolicParameters()) { | ||
130 | parameterMapping.put(constraint.getPatternParameter(), constraint.getParameterVariable()); | ||
131 | } | ||
132 | Set<PVariable> boundVariables = new HashSet<>(); | ||
133 | for (PParameter parameter : boundParameters) { | ||
134 | PVariable mappedParameter = parameterMapping.get(parameter); | ||
135 | boundVariables.add(mappedParameter); | ||
136 | } | ||
137 | return boundVariables; | ||
138 | } | ||
139 | |||
140 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/LocalSearchRuntimeBasedStrategy.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/LocalSearchRuntimeBasedStrategy.java new file mode 100644 index 00000000..e66c2d42 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/LocalSearchRuntimeBasedStrategy.java | |||
@@ -0,0 +1,257 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.planner; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.HashMap; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.interpreter.localsearch.planner.cost.ICostFunction; | ||
20 | import tools.refinery.interpreter.localsearch.planner.util.OperationCostComparator; | ||
21 | import tools.refinery.interpreter.localsearch.matcher.integration.LocalSearchHints; | ||
22 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
23 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
24 | import tools.refinery.interpreter.matchers.planning.SubPlan; | ||
25 | import tools.refinery.interpreter.matchers.planning.SubPlanFactory; | ||
26 | import tools.refinery.interpreter.matchers.planning.operations.PApply; | ||
27 | import tools.refinery.interpreter.matchers.planning.operations.PProject; | ||
28 | import tools.refinery.interpreter.matchers.planning.operations.PStart; | ||
29 | import tools.refinery.interpreter.matchers.psystem.PBody; | ||
30 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
31 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
32 | import tools.refinery.interpreter.matchers.util.Sets; | ||
33 | |||
34 | /** | ||
35 | * This class contains the logic for local search plan calculation based on costs of the operations. | ||
36 | * Its name refers to the fact that the strategy tries to use as much information as available about | ||
37 | * the model on which the matching is initiated. When no runtime info is available, it falls back to | ||
38 | * the information available from the metamodel durint operation cost calculation. | ||
39 | * | ||
40 | * The implementation is based on the paper "Gergely Varró, Frederik Deckwerth, Martin Wieber, and Andy Schürr: | ||
41 | * An algorithm for generating model-sensitive search plans for pattern matching on EMF models" | ||
42 | * (DOI: 10.1007/s10270-013-0372-2) | ||
43 | * | ||
44 | * @author Marton Bur | ||
45 | * @noreference This class is not intended to be referenced by clients. | ||
46 | */ | ||
47 | public class LocalSearchRuntimeBasedStrategy { | ||
48 | |||
49 | private final OperationCostComparator infoComparator = new OperationCostComparator(); | ||
50 | |||
51 | |||
52 | /** | ||
53 | * Converts a plan to the standard format | ||
54 | */ | ||
55 | protected SubPlan convertPlan(Set<PVariable> initialBoundVariables, PlanState searchPlan) { | ||
56 | PBody pBody; | ||
57 | pBody = searchPlan.getAssociatedPBody(); | ||
58 | |||
59 | // Create a starting plan | ||
60 | SubPlanFactory subPlanFactory = new SubPlanFactory(pBody); | ||
61 | |||
62 | // We assume that the adornment (now the bound variables) is previously set | ||
63 | SubPlan plan = subPlanFactory.createSubPlan(new PStart(initialBoundVariables)); | ||
64 | |||
65 | List<PConstraintInfo> operations = searchPlan.getOperations(); | ||
66 | for (PConstraintInfo pConstraintPlanInfo : operations) { | ||
67 | PConstraint pConstraint = pConstraintPlanInfo.getConstraint(); | ||
68 | plan = subPlanFactory.createSubPlan(new PApply(pConstraint), plan); | ||
69 | } | ||
70 | |||
71 | return subPlanFactory.createSubPlan(new PProject(pBody.getSymbolicParameterVariables()), plan); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * The implementation of a local search-based algorithm to create a search plan for a flattened (and normalized) | ||
76 | * PBody | ||
77 | * @param pBody for which the plan is to be created | ||
78 | * @param initialBoundVariables variables that are known to have already assigned values | ||
79 | * @param context the backend context | ||
80 | * @param resultProviderRequestor requestor for accessing result providers of called patterns | ||
81 | * @param configuration the planner configuration | ||
82 | * @return the complete search plan for the given {@link PBody} | ||
83 | * @since 2.1 | ||
84 | */ | ||
85 | protected PlanState plan(PBody pBody, Set<PVariable> initialBoundVariables, | ||
86 | IQueryBackendContext context, final ResultProviderRequestor resultProviderRequestor, | ||
87 | LocalSearchHints configuration) { | ||
88 | final ICostFunction costFunction = configuration.getCostFunction(); | ||
89 | PConstraintInfoInferrer pConstraintInfoInferrer = new PConstraintInfoInferrer( | ||
90 | configuration.isUseBase(), context, resultProviderRequestor, costFunction::apply); | ||
91 | |||
92 | // Create mask infos | ||
93 | Set<PConstraint> constraintSet = pBody.getConstraints(); | ||
94 | List<PConstraintInfo> constraintInfos = | ||
95 | pConstraintInfoInferrer.createPConstraintInfos(constraintSet); | ||
96 | |||
97 | // Calculate the characteristic function | ||
98 | // The characteristic function tells whether a given adornment is backward reachable from the (B)* state, where | ||
99 | // each variable is bound. | ||
100 | // The characteristic function is represented as a set of set of variables | ||
101 | // TODO this calculation is not not implemented yet, thus the contents of the returned set is not considered later | ||
102 | List<Set<PVariable>> reachableBoundVariableSets = reachabilityAnalysis(pBody, constraintInfos); | ||
103 | int k = configuration.getRowCount(); | ||
104 | PlanState searchPlan = calculateSearchPlan(pBody, initialBoundVariables, k, reachableBoundVariableSets, constraintInfos); | ||
105 | return searchPlan; | ||
106 | } | ||
107 | |||
108 | private PlanState calculateSearchPlan(PBody pBody, Set<PVariable> initialBoundVariables, int k, | ||
109 | List<Set<PVariable>> reachableBoundVariableSets, List<PConstraintInfo> allMaskInfos) { | ||
110 | |||
111 | List<PConstraintInfo> allPotentialExtendInfos = new ArrayList<>(); | ||
112 | List<PConstraintInfo> allPotentialCheckInfos = new ArrayList<>(); | ||
113 | Map<PVariable, List<PConstraintInfo>> checkOpsByVariables = new HashMap<>(); | ||
114 | Map<PVariable, Collection<PConstraintInfo>> extendOpsByBoundVariables = new HashMap<>(); | ||
115 | |||
116 | for (PConstraintInfo op : allMaskInfos) { | ||
117 | if (op.getFreeVariables().isEmpty()) { // CHECK | ||
118 | allPotentialCheckInfos.add(op); | ||
119 | } else { // EXTEND | ||
120 | allPotentialExtendInfos.add(op); | ||
121 | for (PVariable variable : op.getBoundVariables()) { | ||
122 | extendOpsByBoundVariables.computeIfAbsent(variable, v -> new ArrayList<>()).add(op); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | // For CHECKs only, we must start from lists that are ordered by the cost of the constraint application | ||
127 | Collections.sort(allPotentialCheckInfos, infoComparator); // costs are eagerly needed for check ops | ||
128 | for (PConstraintInfo op : allPotentialCheckInfos) { | ||
129 | for (PVariable variable : op.getBoundVariables()) { | ||
130 | checkOpsByVariables.computeIfAbsent(variable, v -> new ArrayList<>()).add(op); | ||
131 | } | ||
132 | } | ||
133 | // costs are not needed for extend ops until they are first applied (TODO make cost computaiton on demand) | ||
134 | |||
135 | |||
136 | // rename for better understanding | ||
137 | Set<PVariable> boundVariables = initialBoundVariables; | ||
138 | Set<PVariable> freeVariables = Sets.difference(pBody.getUniqueVariables(), initialBoundVariables); | ||
139 | |||
140 | int variableCount = pBody.getUniqueVariables().size(); | ||
141 | int n = freeVariables.size(); | ||
142 | |||
143 | List<List<PlanState>> stateTable = initializeStateTable(k, n); | ||
144 | |||
145 | // Set initial state: begin with an empty operation list | ||
146 | PlanState initialState = new PlanState(pBody, boundVariables); | ||
147 | |||
148 | // Initial state creation, categorizes all operations; add present checks to operationsList | ||
149 | initialState.updateExtends(allPotentialExtendInfos); | ||
150 | initialState.applyChecks(allPotentialCheckInfos); | ||
151 | stateTable.get(n).add(0, initialState); | ||
152 | |||
153 | // stateTable.get(0) will contain the states with adornment B* | ||
154 | for (int i = n; i > 0; i--) { | ||
155 | for (int j = 0; j < k && j < stateTable.get(i).size(); j++) { | ||
156 | PlanState currentState = stateTable.get(i).get(j); | ||
157 | |||
158 | for (PConstraintInfo constraintInfo : currentState.getPresentExtends()) { | ||
159 | // for each present EXTEND operation | ||
160 | PlanState newState = calculateNextState(currentState, constraintInfo); | ||
161 | // also eagerly perform any CHECK operations that become applicable (extends still deferred) | ||
162 | newState.applyChecksBasedOnDelta(checkOpsByVariables); | ||
163 | |||
164 | if(currentState.getBoundVariables().size() == newState.getBoundVariables().size()){ | ||
165 | // This means no variable binding was done, go on with the next constraint info | ||
166 | continue; | ||
167 | } | ||
168 | int i2 = variableCount - newState.getBoundVariables().size(); | ||
169 | |||
170 | List<Integer> newIndices = determineIndices(stateTable, i2, newState, k); | ||
171 | int a = newIndices.get(0); | ||
172 | int c = newIndices.get(1); | ||
173 | |||
174 | if (checkInsertCondition(stateTable.get(i2), newState, reachableBoundVariableSets, a, c, k)) { | ||
175 | updateExtends(newState, currentState, extendOpsByBoundVariables); // preprocess next steps | ||
176 | insert(stateTable,i2, newState, a, c, k); | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | |||
182 | return stateTable.get(0).get(0); | ||
183 | } | ||
184 | |||
185 | private List<List<PlanState>> initializeStateTable(int k, int n) { | ||
186 | List<List<PlanState>> stateTable = new ArrayList<>(); | ||
187 | // Initialize state table and fill it with null | ||
188 | for (int i = 0; i <= n ; i++) { | ||
189 | stateTable.add(new ArrayList<>()); | ||
190 | } | ||
191 | return stateTable; | ||
192 | } | ||
193 | |||
194 | private void insert(List<List<PlanState>> stateTable, int idx, PlanState newState, int a, int c, int k) { | ||
195 | stateTable.get(idx).add(c, newState); | ||
196 | while(stateTable.get(idx).size() > k){ | ||
197 | // Truncate back to size k when grows too big | ||
198 | stateTable.set(idx, stateTable.get(idx).subList(0, k)); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | private void updateExtends(PlanState newState, PlanState currentState, | ||
203 | Map<PVariable, ? extends Collection<PConstraintInfo>> extendOpsByBoundVariables) | ||
204 | { | ||
205 | List<PConstraintInfo> presentExtends = currentState.getPresentExtends(); | ||
206 | |||
207 | // Recategorize operations | ||
208 | newState.updateExtendsBasedOnDelta(presentExtends, extendOpsByBoundVariables); | ||
209 | |||
210 | return; | ||
211 | } | ||
212 | |||
213 | private boolean checkInsertCondition(List<PlanState> list, PlanState newState, | ||
214 | List<Set<PVariable>> reachableBoundVariableSets, int a, int c, int k) { | ||
215 | // boolean isAmongBestK = (a == (k + 1)) && c < a && reachableBoundVariableSets.contains(newState.getBoundVariables()); | ||
216 | boolean isAmongBestK = a == k && c < a ; | ||
217 | boolean isBetterThanCurrent = a < k && c <= a; | ||
218 | |||
219 | return isAmongBestK || isBetterThanCurrent; | ||
220 | } | ||
221 | |||
222 | private List<Integer> determineIndices(List<List<PlanState>> stateTable, int i2, PlanState newState, int k) { | ||
223 | int a = k; | ||
224 | int c = 0; | ||
225 | List<Integer> acIndices = new ArrayList<>(); | ||
226 | for (int j = 0; j < k; j++) { | ||
227 | if (j < stateTable.get(i2).size()) { | ||
228 | PlanState stateInTable = stateTable.get(i2).get(j); | ||
229 | if (newState.getBoundVariables().equals(stateInTable.getBoundVariables())) { | ||
230 | // The new state has the same adornment as the stored one - they are not adornment disjoint | ||
231 | a = j; | ||
232 | } | ||
233 | if (newState.getCost() >= stateInTable.getCost()) { | ||
234 | c = j + 1; | ||
235 | } | ||
236 | } else { | ||
237 | break; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | acIndices.add(a); | ||
242 | acIndices.add(c); | ||
243 | return acIndices; | ||
244 | } | ||
245 | |||
246 | private PlanState calculateNextState(PlanState currentState, PConstraintInfo constraintInfo) { | ||
247 | return currentState.cloneWithApplied(constraintInfo); | ||
248 | } | ||
249 | |||
250 | private List<Set<PVariable>> reachabilityAnalysis(PBody pBody, List<PConstraintInfo> constraintInfos) { | ||
251 | // TODO implement reachability analisys, also save/persist the results somewhere | ||
252 | List<Set<PVariable>> reachableBoundVariableSets = new ArrayList<>(); | ||
253 | return reachableBoundVariableSets; | ||
254 | } | ||
255 | |||
256 | |||
257 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintCategory.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintCategory.java new file mode 100644 index 00000000..2f5d8d1f --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintCategory.java | |||
@@ -0,0 +1,41 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.interpreter.localsearch.planner; | ||
10 | |||
11 | |||
12 | /** | ||
13 | * Expresses the state of a PConstraint application | ||
14 | * condition with respect to a given adornment. | ||
15 | * | ||
16 | * @author Marton Bur | ||
17 | * @noreference This enum is not intended to be referenced by clients. | ||
18 | */ | ||
19 | public enum PConstraintCategory { | ||
20 | /* | ||
21 | * During plan creation an operation is considered a past | ||
22 | * operation, if an already bound variable is free in the | ||
23 | * mask of the operation. | ||
24 | * (Mask of the operation: the required binding state of | ||
25 | * the affected variables) | ||
26 | */ | ||
27 | PAST, | ||
28 | /* | ||
29 | * The binding states of the variables in the operation | ||
30 | * mask correspond to the current binding states of the | ||
31 | * variables in the search plan | ||
32 | */ | ||
33 | PRESENT, | ||
34 | /* | ||
35 | * There is at least one bound variable in the mask of | ||
36 | * a future operation that is still free at the current | ||
37 | * state of the plan. Also, a future operation can't be | ||
38 | * PAST. | ||
39 | */ | ||
40 | FUTURE; | ||
41 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintInfo.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintInfo.java new file mode 100644 index 00000000..6c42c180 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintInfo.java | |||
@@ -0,0 +1,142 @@ | |||
1 | /** | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Danil 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.interpreter.localsearch.planner; | ||
10 | |||
11 | import java.util.Collections; | ||
12 | import java.util.LinkedHashSet; | ||
13 | import java.util.Set; | ||
14 | import java.util.function.Function; | ||
15 | |||
16 | import tools.refinery.interpreter.localsearch.planner.cost.IConstraintEvaluationContext; | ||
17 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
18 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
19 | import tools.refinery.interpreter.matchers.context.IQueryResultProviderAccess; | ||
20 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
21 | import tools.refinery.interpreter.matchers.psystem.PBody; | ||
22 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
23 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
24 | import tools.refinery.interpreter.matchers.psystem.analysis.QueryAnalyzer; | ||
25 | |||
26 | /** | ||
27 | * Wraps a PConstraint together with information required for the planner. Currently contains information about the expected binding state of | ||
28 | * the affected variables also called application condition, and the cost of the enforcement, based on the meta and/or the runtime context. | ||
29 | * | ||
30 | * @author Marton Bur | ||
31 | * @noreference This class is not intended to be referenced by clients. | ||
32 | */ | ||
33 | public class PConstraintInfo implements IConstraintEvaluationContext { | ||
34 | |||
35 | private PConstraint constraint; | ||
36 | private Set<PVariable> boundMaskVariables; | ||
37 | private Set<PVariable> freeMaskVariables; | ||
38 | private Set<PConstraintInfo> sameWithDifferentBindings; | ||
39 | private IQueryRuntimeContext runtimeContext; | ||
40 | private QueryAnalyzer queryAnalyzer; | ||
41 | private IQueryResultProviderAccess resultProviderAccess; | ||
42 | private ResultProviderRequestor resultRequestor; | ||
43 | |||
44 | private Double cost; | ||
45 | private Function<IConstraintEvaluationContext, Double> costFunction; | ||
46 | |||
47 | |||
48 | /** | ||
49 | * Instantiates the wrapper | ||
50 | * @param constraintfor which the information is added and stored | ||
51 | * @param boundMaskVariables the bound variables in the operation mask | ||
52 | * @param freeMaskVariables the free variables in the operation mask | ||
53 | * @param sameWithDifferentBindings during the planning process, multiple operation adornments are considered for a constraint, so that it | ||
54 | * is represented by multiple plan infos. This parameter contains all plan infos that are for the same | ||
55 | * constraint, but with different adornment | ||
56 | * @param context the query backend context | ||
57 | */ | ||
58 | public PConstraintInfo(PConstraint constraint, Set<PVariable> boundMaskVariables, Set<PVariable> freeMaskVariables, | ||
59 | Set<PConstraintInfo> sameWithDifferentBindings, | ||
60 | IQueryBackendContext context, | ||
61 | ResultProviderRequestor resultRequestor, | ||
62 | Function<IConstraintEvaluationContext, Double> costFunction) { | ||
63 | this.constraint = constraint; | ||
64 | this.costFunction = costFunction; | ||
65 | this.boundMaskVariables = new LinkedHashSet<>(boundMaskVariables); | ||
66 | this.freeMaskVariables = new LinkedHashSet<>(freeMaskVariables); | ||
67 | this.sameWithDifferentBindings = sameWithDifferentBindings; | ||
68 | this.resultRequestor = resultRequestor; | ||
69 | this.runtimeContext = context.getRuntimeContext(); | ||
70 | this.queryAnalyzer = context.getQueryAnalyzer(); | ||
71 | this.resultProviderAccess = context.getResultProviderAccess(); | ||
72 | |||
73 | this.cost = null; // cost will be computed lazily (esp. important for pattern calls) | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public IQueryRuntimeContext getRuntimeContext() { | ||
78 | return runtimeContext; | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public QueryAnalyzer getQueryAnalyzer() { | ||
83 | return queryAnalyzer; | ||
84 | } | ||
85 | |||
86 | @Override | ||
87 | public PConstraint getConstraint() { | ||
88 | return constraint; | ||
89 | } | ||
90 | |||
91 | @Override | ||
92 | public Set<PVariable> getFreeVariables() { | ||
93 | return freeMaskVariables; | ||
94 | } | ||
95 | |||
96 | @Override | ||
97 | public Set<PVariable> getBoundVariables() { | ||
98 | return boundMaskVariables; | ||
99 | } | ||
100 | |||
101 | public Set<PConstraintInfo> getSameWithDifferentBindings() { | ||
102 | return sameWithDifferentBindings; | ||
103 | } | ||
104 | |||
105 | public double getCost() { | ||
106 | if (cost == null) { | ||
107 | // Calculate cost of the constraint based on its type | ||
108 | cost = costFunction.apply(this); | ||
109 | } | ||
110 | return cost; | ||
111 | } | ||
112 | |||
113 | public PConstraintCategory getCategory(PBody pBody, Set<PVariable> boundVariables) { | ||
114 | if (!Collections.disjoint(boundVariables, this.freeMaskVariables)) { | ||
115 | return PConstraintCategory.PAST; | ||
116 | } else if (!boundVariables.containsAll(this.boundMaskVariables)) { | ||
117 | return PConstraintCategory.FUTURE; | ||
118 | } else { | ||
119 | return PConstraintCategory.PRESENT; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | @Override | ||
124 | public String toString() { | ||
125 | return String.format("%s, bound variables: %s, cost: \"%.2f\"", constraint.toString(), boundMaskVariables.toString(), cost); | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * @deprecated use {@link #resultProviderRequestor()} | ||
130 | */ | ||
131 | @Override | ||
132 | @Deprecated | ||
133 | public IQueryResultProviderAccess resultProviderAccess() { | ||
134 | return resultProviderAccess; | ||
135 | } | ||
136 | |||
137 | @Override | ||
138 | public ResultProviderRequestor resultProviderRequestor() { | ||
139 | return resultRequestor; | ||
140 | } | ||
141 | |||
142 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintInfoInferrer.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintInfoInferrer.java new file mode 100644 index 00000000..db9fcd8e --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PConstraintInfoInferrer.java | |||
@@ -0,0 +1,278 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.planner; | ||
10 | |||
11 | import tools.refinery.interpreter.localsearch.planner.cost.IConstraintEvaluationContext; | ||
12 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
13 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
14 | import tools.refinery.interpreter.matchers.context.IQueryBackendContext; | ||
15 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
17 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.*; | ||
18 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.AbstractTransitiveClosure; | ||
19 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.ConstantValue; | ||
20 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
21 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.TypeConstraint; | ||
22 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
23 | import tools.refinery.interpreter.matchers.psystem.queries.PParameterDirection; | ||
24 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
25 | import tools.refinery.interpreter.matchers.util.Sets; | ||
26 | |||
27 | import java.util.*; | ||
28 | import java.util.function.Function; | ||
29 | import java.util.function.Predicate; | ||
30 | import java.util.stream.Collectors; | ||
31 | import java.util.stream.Stream; | ||
32 | |||
33 | |||
34 | /** | ||
35 | * @author Grill Balázs | ||
36 | * @noreference This class is not intended to be referenced by clients. | ||
37 | */ | ||
38 | class PConstraintInfoInferrer { | ||
39 | |||
40 | private static final Predicate<PVariable> SINGLE_USE_VARIABLE = input -> input != null && input.getReferringConstraints().size() == 1; | ||
41 | |||
42 | private final boolean useIndex; | ||
43 | private final Function<IConstraintEvaluationContext, Double> costFunction; | ||
44 | private final IQueryBackendContext context; | ||
45 | private final ResultProviderRequestor resultRequestor; | ||
46 | |||
47 | |||
48 | public PConstraintInfoInferrer(boolean useIndex, | ||
49 | IQueryBackendContext backendContext, | ||
50 | ResultProviderRequestor resultRequestor, | ||
51 | Function<IConstraintEvaluationContext, Double> costFunction) { | ||
52 | this.useIndex = useIndex; | ||
53 | this.context = backendContext; | ||
54 | this.resultRequestor = resultRequestor; | ||
55 | this.costFunction = costFunction; | ||
56 | } | ||
57 | |||
58 | |||
59 | /** | ||
60 | * Create all possible application condition for all constraint | ||
61 | * | ||
62 | * @param constraintSet the set of constraints | ||
63 | * @return a collection of the wrapper PConstraintInfo objects with all the allowed application conditions | ||
64 | */ | ||
65 | public List<PConstraintInfo> createPConstraintInfos(Set<PConstraint> constraintSet) { | ||
66 | List<PConstraintInfo> constraintInfos = new ArrayList<>(); | ||
67 | |||
68 | for (PConstraint pConstraint : constraintSet) { | ||
69 | createPConstraintInfoDispatch(constraintInfos, pConstraint); | ||
70 | } | ||
71 | return constraintInfos; | ||
72 | } | ||
73 | |||
74 | private void createPConstraintInfoDispatch(List<PConstraintInfo> resultList, PConstraint pConstraint){ | ||
75 | if(pConstraint instanceof ExportedParameter){ | ||
76 | createConstraintInfoExportedParameter(resultList, (ExportedParameter) pConstraint); | ||
77 | } else if(pConstraint instanceof TypeConstraint){ | ||
78 | createConstraintInfoTypeConstraint(resultList, (TypeConstraint)pConstraint); | ||
79 | } else if(pConstraint instanceof TypeFilterConstraint){ | ||
80 | createConstraintInfoTypeFilterConstraint(resultList, (TypeFilterConstraint)pConstraint); | ||
81 | } else if(pConstraint instanceof ConstantValue){ | ||
82 | createConstraintInfoConstantValue(resultList, (ConstantValue)pConstraint); | ||
83 | } else if (pConstraint instanceof Inequality){ | ||
84 | createConstraintInfoInequality(resultList, (Inequality) pConstraint); | ||
85 | } else if (pConstraint instanceof ExpressionEvaluation){ | ||
86 | createConstraintInfoExpressionEvaluation(resultList, (ExpressionEvaluation)pConstraint); | ||
87 | } else if (pConstraint instanceof AggregatorConstraint){ | ||
88 | createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((AggregatorConstraint) pConstraint).getResultVariable()); | ||
89 | } else if (pConstraint instanceof PatternMatchCounter){ | ||
90 | createConstraintInfoAggregatorConstraint(resultList, pConstraint, ((PatternMatchCounter) pConstraint).getResultVariable()); | ||
91 | } else if (pConstraint instanceof PositivePatternCall){ | ||
92 | createConstraintInfoPositivePatternCall(resultList, (PositivePatternCall) pConstraint); | ||
93 | } else if (pConstraint instanceof AbstractTransitiveClosure) { | ||
94 | createConstraintInfoBinaryTransitiveClosure(resultList, (AbstractTransitiveClosure) pConstraint); | ||
95 | } else{ | ||
96 | createConstraintInfoGeneric(resultList, pConstraint); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | private void createConstraintInfoConstantValue(List<PConstraintInfo> resultList, | ||
101 | ConstantValue pConstraint) { | ||
102 | // A ConstantValue constraint has a single variable, which is allowed to be unbound | ||
103 | // (extending through ConstantValue is considered a cheap operation) | ||
104 | Set<PVariable> affectedVariables = pConstraint.getAffectedVariables(); | ||
105 | Set<? extends Set<PVariable>> bindings = Sets.powerSet(affectedVariables); | ||
106 | doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings); | ||
107 | } | ||
108 | |||
109 | |||
110 | private void createConstraintInfoPositivePatternCall(List<PConstraintInfo> resultList, | ||
111 | PositivePatternCall pCall) { | ||
112 | // A pattern call can have any of its variables unbound | ||
113 | Set<PVariable> affectedVariables = pCall.getAffectedVariables(); | ||
114 | // IN parameters cannot be unbound and | ||
115 | // OUT parameters cannot be bound | ||
116 | Tuple variables = pCall.getVariablesTuple(); | ||
117 | final Set<PVariable> inVariables = new HashSet<>(); | ||
118 | Set<PVariable> inoutVariables = new HashSet<>(); | ||
119 | List<PParameter> parameters = pCall.getReferredQuery().getParameters(); | ||
120 | for(int i=0;i<parameters.size();i++){ | ||
121 | switch(parameters.get(i).getDirection()){ | ||
122 | case IN: | ||
123 | inVariables.add((PVariable) variables.get(i)); | ||
124 | break; | ||
125 | case INOUT: | ||
126 | inoutVariables.add((PVariable) variables.get(i)); | ||
127 | break; | ||
128 | case OUT: | ||
129 | default: | ||
130 | break; | ||
131 | |||
132 | } | ||
133 | } | ||
134 | Iterable<Set<PVariable>> bindings = Sets.powerSet(inoutVariables).stream() | ||
135 | .map(input -> Stream.concat(input.stream(), inVariables.stream()).collect(Collectors.toSet())) | ||
136 | .collect(Collectors.toSet()); | ||
137 | |||
138 | doCreateConstraintInfos(resultList, pCall, affectedVariables, bindings); | ||
139 | } | ||
140 | |||
141 | private void createConstraintInfoBinaryTransitiveClosure(List<PConstraintInfo> resultList, | ||
142 | AbstractTransitiveClosure closure) { | ||
143 | // A pattern call can have any of its variables unbound | ||
144 | |||
145 | List<PParameter> parameters = closure.getReferredQuery().getParameters(); | ||
146 | Tuple variables = closure.getVariablesTuple(); | ||
147 | |||
148 | Set<Set<PVariable>> bindings = new HashSet<>(); | ||
149 | PVariable firstVariable = (PVariable) variables.get(0); | ||
150 | PVariable secondVariable = (PVariable) variables.get(1); | ||
151 | // Check is always supported | ||
152 | bindings.add(new HashSet<>(Arrays.asList(firstVariable, secondVariable))); | ||
153 | // If first parameter is not bound mandatorily, it can be left out | ||
154 | if (parameters.get(0).getDirection() != PParameterDirection.IN) { | ||
155 | bindings.add(Collections.singleton(secondVariable)); | ||
156 | } | ||
157 | // If second parameter is not bound mandatorily, it can be left out | ||
158 | if (parameters.get(1).getDirection() != PParameterDirection.IN) { | ||
159 | bindings.add(Collections.singleton(firstVariable)); | ||
160 | } | ||
161 | |||
162 | doCreateConstraintInfos(resultList, closure, closure.getAffectedVariables(), bindings); | ||
163 | } | ||
164 | |||
165 | |||
166 | |||
167 | private void createConstraintInfoExportedParameter(List<PConstraintInfo> resultList, | ||
168 | ExportedParameter parameter) { | ||
169 | // In case of an exported parameter constraint, the parameter must be bound in order to execute | ||
170 | Set<PVariable> affectedVariables = parameter.getAffectedVariables(); | ||
171 | doCreateConstraintInfos(resultList, parameter, affectedVariables, Collections.singleton(affectedVariables)); | ||
172 | } | ||
173 | |||
174 | private void createConstraintInfoExpressionEvaluation(List<PConstraintInfo> resultList, | ||
175 | ExpressionEvaluation expressionEvaluation) { | ||
176 | // An expression evaluation can only have its output variable unbound. All other variables shall be bound | ||
177 | PVariable output = expressionEvaluation.getOutputVariable(); | ||
178 | Set<Set<PVariable>> bindings = new HashSet<>(); | ||
179 | Set<PVariable> affectedVariables = expressionEvaluation.getAffectedVariables(); | ||
180 | // All variables bound -> check | ||
181 | bindings.add(affectedVariables); | ||
182 | // Output variable is not bound -> extend | ||
183 | bindings.add(affectedVariables.stream().filter(var -> !Objects.equals(var, output)).collect(Collectors.toSet())); | ||
184 | doCreateConstraintInfos(resultList, expressionEvaluation, affectedVariables, bindings); | ||
185 | } | ||
186 | |||
187 | private void createConstraintInfoTypeFilterConstraint(List<PConstraintInfo> resultList, | ||
188 | TypeFilterConstraint filter){ | ||
189 | // In case of type filter, all affected variables must be bound in order to execute | ||
190 | Set<PVariable> affectedVariables = filter.getAffectedVariables(); | ||
191 | doCreateConstraintInfos(resultList, filter, affectedVariables, Collections.singleton(affectedVariables)); | ||
192 | } | ||
193 | |||
194 | private void createConstraintInfoInequality(List<PConstraintInfo> resultList, | ||
195 | Inequality inequality){ | ||
196 | // In case of inequality, all affected variables must be bound in order to execute | ||
197 | Set<PVariable> affectedVariables = inequality.getAffectedVariables(); | ||
198 | doCreateConstraintInfos(resultList, inequality, affectedVariables, Collections.singleton(affectedVariables)); | ||
199 | } | ||
200 | |||
201 | private void createConstraintInfoAggregatorConstraint(List<PConstraintInfo> resultList, | ||
202 | PConstraint pConstraint, PVariable resultVariable){ | ||
203 | Set<PVariable> affectedVariables = pConstraint.getAffectedVariables(); | ||
204 | |||
205 | // The only variables which can be unbound are single-use | ||
206 | Set<PVariable> canBeUnboundVariables = | ||
207 | Stream.concat(Stream.of(resultVariable), affectedVariables.stream().filter(SINGLE_USE_VARIABLE)).collect(Collectors.toSet()); | ||
208 | |||
209 | Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables); | ||
210 | |||
211 | doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings); | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * | ||
216 | * @param canBeUnboundVariables Variables which are allowed to be unbound | ||
217 | * @param affectedVariables All affected variables | ||
218 | * @return The set of possible bound variable sets | ||
219 | */ | ||
220 | private Set<Set<PVariable>> calculatePossibleBindings(Set<PVariable> canBeUnboundVariables, Set<PVariable> affectedVariables){ | ||
221 | final Set<PVariable> mustBindVariables = affectedVariables.stream().filter(input -> !canBeUnboundVariables.contains(input)).collect(Collectors.toSet()); | ||
222 | return Sets.powerSet(canBeUnboundVariables).stream() | ||
223 | .map(input -> { | ||
224 | //some variables have to be bound before executing this constraint | ||
225 | Set<PVariable> result= new HashSet<>(input); | ||
226 | result.addAll(mustBindVariables); | ||
227 | return result; | ||
228 | }) | ||
229 | .collect(Collectors.toSet()); | ||
230 | } | ||
231 | |||
232 | private void createConstraintInfoGeneric(List<PConstraintInfo> resultList, PConstraint pConstraint){ | ||
233 | Set<PVariable> affectedVariables = pConstraint.getAffectedVariables(); | ||
234 | |||
235 | // The only variables which can be unbound are single use variables | ||
236 | Set<PVariable> canBeUnboundVariables = affectedVariables.stream().filter(SINGLE_USE_VARIABLE).collect(Collectors.toSet()); | ||
237 | |||
238 | Set<Set<PVariable>> bindings = calculatePossibleBindings(canBeUnboundVariables, affectedVariables); | ||
239 | |||
240 | doCreateConstraintInfos(resultList, pConstraint, affectedVariables, bindings); | ||
241 | } | ||
242 | |||
243 | private void createConstraintInfoTypeConstraint(List<PConstraintInfo> resultList, | ||
244 | TypeConstraint typeConstraint) { | ||
245 | Set<PVariable> affectedVariables = typeConstraint.getAffectedVariables(); | ||
246 | Set<? extends Set<PVariable>> bindings = null; | ||
247 | |||
248 | IInputKey inputKey = typeConstraint.getSupplierKey(); | ||
249 | if(inputKey.isEnumerable()){ | ||
250 | bindings = Sets.powerSet(affectedVariables); | ||
251 | }else{ | ||
252 | // For not enumerable types, this constraint can only be a check | ||
253 | bindings = Collections.singleton(affectedVariables); | ||
254 | } | ||
255 | |||
256 | doCreateConstraintInfos(resultList, typeConstraint, affectedVariables, bindings); | ||
257 | } | ||
258 | |||
259 | private void doCreateConstraintInfos(List<PConstraintInfo> constraintInfos, | ||
260 | PConstraint pConstraint, Set<PVariable> affectedVariables, Iterable<? extends Set<PVariable>> bindings) { | ||
261 | Set<PConstraintInfo> sameWithDifferentBindings = new HashSet<>(); | ||
262 | for (Set<PVariable> boundVariables : bindings) { | ||
263 | |||
264 | PConstraintInfo info = new PConstraintInfo(pConstraint, boundVariables, | ||
265 | affectedVariables.stream().filter(input -> !boundVariables.contains(input)).collect(Collectors.toSet()), | ||
266 | sameWithDifferentBindings, context, resultRequestor, costFunction); | ||
267 | constraintInfos.add(info); | ||
268 | sameWithDifferentBindings.add(info); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | private Set<Set<PVariable>> excludeUnnavigableOperationMasks(TypeConstraint typeConstraint, Set<? extends Set<PVariable>> bindings) { | ||
273 | PVariable firstVariable = typeConstraint.getVariableInTuple(0); | ||
274 | return bindings.stream().filter( | ||
275 | boundVariablesSet -> (boundVariablesSet.isEmpty() || boundVariablesSet.contains(firstVariable))) | ||
276 | .collect(Collectors.toSet()); | ||
277 | } | ||
278 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PlanState.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PlanState.java new file mode 100644 index 00000000..b3dd5290 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/PlanState.java | |||
@@ -0,0 +1,283 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, 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.interpreter.localsearch.planner; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collection; | ||
13 | import java.util.Collections; | ||
14 | import java.util.Comparator; | ||
15 | import java.util.HashSet; | ||
16 | import java.util.List; | ||
17 | import java.util.Map; | ||
18 | import java.util.Set; | ||
19 | |||
20 | import tools.refinery.interpreter.localsearch.planner.util.OperationCostComparator; | ||
21 | import tools.refinery.interpreter.matchers.algorithms.OrderedIterableMerge; | ||
22 | import tools.refinery.interpreter.matchers.psystem.PBody; | ||
23 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
24 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
25 | |||
26 | /** | ||
27 | * This class represents the state of the plan during planning. | ||
28 | * | ||
29 | * <p> A PlanState represents a sequence of operations (operationsList) and caches the computed cost | ||
30 | * for this operation sequence. The list and the cost are initialized in the constructor. | ||
31 | * However, #categorizeChecks() also updates the operations list (by suffixing checks) | ||
32 | * | ||
33 | * @author Marton Bur | ||
34 | * @noreference This class is not intended to be referenced by clients. | ||
35 | */ | ||
36 | public class PlanState { | ||
37 | |||
38 | private final PBody pBody; | ||
39 | private final List<PConstraintInfo> operationsList; | ||
40 | private final Set<PVariable> boundVariables; | ||
41 | private final Collection<PVariable> deltaVariables; /* bound since ancestor plan */ | ||
42 | private final Set<PConstraint> enforcedConstraints; | ||
43 | |||
44 | private double cummulativeProduct; | ||
45 | private double cost; | ||
46 | |||
47 | private static Comparator<PConstraintInfo> infoComparator = new OperationCostComparator(); | ||
48 | |||
49 | /* | ||
50 | * For a short explanation of past, present and future operations, | ||
51 | * see class | ||
52 | */ | ||
53 | private List<PConstraintInfo> presentExtends; | ||
54 | |||
55 | /** | ||
56 | * Creates an initial state | ||
57 | */ | ||
58 | public PlanState(PBody pBody, Set<PVariable> boundVariables) { | ||
59 | |||
60 | this(pBody, new ArrayList<>(), boundVariables, boundVariables /* also the delta */, | ||
61 | 0.0 /* initial cost */, 1.0 /*initial branch count */); | ||
62 | } | ||
63 | |||
64 | public PlanState cloneWithApplied(PConstraintInfo op) { | ||
65 | // Create operation list based on the current state | ||
66 | ArrayList<PConstraintInfo> newOperationsList = | ||
67 | // pre-reserve excess capacity for later addition of CHECK ops | ||
68 | new ArrayList<>(pBody.getConstraints().size()); | ||
69 | newOperationsList.addAll(this.getOperations()); | ||
70 | newOperationsList.add(op); | ||
71 | |||
72 | // Bind the variables of the op | ||
73 | Collection<PVariable> deltaVariables = op.getFreeVariables(); | ||
74 | Set<PVariable> allBoundVariables = | ||
75 | // pre-reserve exact capacity as variables are known | ||
76 | // (will not be affected by adding CHECK ops later) | ||
77 | new HashSet<>(this.getBoundVariables().size() + deltaVariables.size()); | ||
78 | allBoundVariables.addAll(this.getBoundVariables()); | ||
79 | allBoundVariables.addAll(deltaVariables); | ||
80 | |||
81 | PlanState newState = new PlanState(getAssociatedPBody(), newOperationsList, allBoundVariables, deltaVariables, | ||
82 | cost, cummulativeProduct); | ||
83 | newState.accountNewOperation(op); | ||
84 | return newState; | ||
85 | } | ||
86 | |||
87 | private PlanState(PBody pBody, List<PConstraintInfo> operationsList, | ||
88 | Set<PVariable> boundVariables, Collection<PVariable> deltaVariables, | ||
89 | double cost, double cummulativeProduct) | ||
90 | { | ||
91 | this.pBody = pBody; | ||
92 | this.operationsList = operationsList; | ||
93 | this.boundVariables = boundVariables; | ||
94 | this.enforcedConstraints = new HashSet<>(); | ||
95 | this.deltaVariables = deltaVariables; | ||
96 | this.cost = cost; | ||
97 | this.cummulativeProduct = cummulativeProduct; | ||
98 | } | ||
99 | |||
100 | // NOT included for EXTEND: bind all variables of op | ||
101 | private void accountNewOperation(PConstraintInfo constraintInfo) { | ||
102 | this.enforcedConstraints.add(constraintInfo.getConstraint()); | ||
103 | accountCost(constraintInfo); | ||
104 | } | ||
105 | |||
106 | private void accountCost(PConstraintInfo constraintInfo) { | ||
107 | double constraintCost = constraintInfo.getCost(); | ||
108 | double branchFactor = constraintCost; | ||
109 | if (constraintCost > 0){ | ||
110 | cost += cummulativeProduct * constraintCost; | ||
111 | cummulativeProduct *= branchFactor; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | |||
116 | public Set<PConstraint> getEnforcedConstraints() { | ||
117 | return enforcedConstraints; | ||
118 | } | ||
119 | |||
120 | /** | ||
121 | * Re-categorizes given extend operations into already applied or no longer applicable ones (discarded), | ||
122 | * immediately applicable ones (saved as presently viable extends), | ||
123 | * and not yet applicable ones (discarded). | ||
124 | * | ||
125 | * @param allPotentialExtendInfos all other extends that may be applicable | ||
126 | * to this plan state now or in the future; | ||
127 | * MUST consist of "extend" constraint applications only (at least one free variable) | ||
128 | */ | ||
129 | public void updateExtends(Iterable<PConstraintInfo> allPotentialExtendInfos) { | ||
130 | presentExtends = new ArrayList<>(); | ||
131 | |||
132 | |||
133 | // categorize future/present extend constraint infos | ||
134 | for (PConstraintInfo op : allPotentialExtendInfos) { | ||
135 | updateExtendInternal(op); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /** | ||
140 | * Re-categorizes given extend operations into already applied or no longer applicable ones (discarded), | ||
141 | * immediately applicable ones (saved as presently viable extends), | ||
142 | * and not yet applicable ones (discarded). | ||
143 | * | ||
144 | * @param extendOpsByBoundVariables all EXTEND operations indexed by affected <i>bound</i> variables | ||
145 | * MUST consist of "extend" constraint applications only (at least one free variable) | ||
146 | */ | ||
147 | public void updateExtendsBasedOnDelta( | ||
148 | Iterable<PConstraintInfo> previousPresentExtends, | ||
149 | Map<PVariable, ? extends Collection<PConstraintInfo>> extendOpsByBoundVariables) | ||
150 | { | ||
151 | presentExtends = new ArrayList<>(); | ||
152 | if (operationsList.isEmpty()) | ||
153 | throw new IllegalStateException("Not applicable as starting step"); | ||
154 | |||
155 | for (PConstraintInfo extend: previousPresentExtends) { | ||
156 | updateExtendInternal(extend); | ||
157 | } | ||
158 | |||
159 | Set<PConstraintInfo> affectedExtends = new HashSet<>(); | ||
160 | for (PVariable variable : deltaVariables) { | ||
161 | // only those check ops may become applicable that have an affected variable in the delta | ||
162 | Collection<PConstraintInfo> extendsForVariable = extendOpsByBoundVariables.get(variable); | ||
163 | if (null != extendsForVariable) { | ||
164 | affectedExtends.addAll(extendsForVariable); | ||
165 | } | ||
166 | } | ||
167 | for (PConstraintInfo extend: affectedExtends) { | ||
168 | updateExtendInternal(extend); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | private void updateExtendInternal(PConstraintInfo op) { | ||
173 | if(!enforcedConstraints.contains(op.getConstraint())) { | ||
174 | categorizeExtend(op); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * Check operations that newly became applicable (see {@link #getDeltaVariables()}) | ||
180 | * are appended to operations lists. | ||
181 | * | ||
182 | * <p> Will never discover degenerate checks (of PConstraints with zero variables), | ||
183 | * so must not use on initial state. | ||
184 | * | ||
185 | * @param allPotentialCheckInfos all CHECK operations | ||
186 | * MUST consist of "check" constraint applications only (no free variables) | ||
187 | * and must be iterable in decreasing order of cost | ||
188 | * | ||
189 | * | ||
190 | */ | ||
191 | public void applyChecks(List<PConstraintInfo> allPotentialCheckInfos) { | ||
192 | applyChecksInternal(allPotentialCheckInfos); | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * Immediately applicable checks are appended to operations lists. | ||
197 | * | ||
198 | * @param checkOpsByVariables all CHECK operations indexed by affected variables | ||
199 | * MUST consist of "check" constraint applications only (no free variables) | ||
200 | * and each bucket must be iterable in decreasing order of cost | ||
201 | */ | ||
202 | public void applyChecksBasedOnDelta(Map<PVariable, List<PConstraintInfo>> checkOpsByVariables) { | ||
203 | if (operationsList.isEmpty()) | ||
204 | throw new IllegalStateException("Not applicable as starting step"); | ||
205 | |||
206 | Iterable<PConstraintInfo> affectedChecks = Collections.emptyList(); | ||
207 | |||
208 | for (PVariable variable : deltaVariables) { | ||
209 | // only those check ops may become applicable that have an affected variable in the delta | ||
210 | List<PConstraintInfo> checksForVariable = checkOpsByVariables.get(variable); | ||
211 | if (null != checksForVariable) { | ||
212 | affectedChecks = OrderedIterableMerge.mergeUniques(affectedChecks, checksForVariable, infoComparator); | ||
213 | } | ||
214 | } | ||
215 | |||
216 | // checks retain their order, no re-sorting needed | ||
217 | applyChecksInternal(affectedChecks); | ||
218 | } | ||
219 | |||
220 | private void applyChecksInternal(Iterable<PConstraintInfo> checks) { | ||
221 | for (PConstraintInfo checkInfo : checks) { | ||
222 | if (this.boundVariables.containsAll(checkInfo.getBoundVariables()) && | ||
223 | !enforcedConstraints.contains(checkInfo.getConstraint())) | ||
224 | { | ||
225 | operationsList.add(checkInfo); | ||
226 | accountNewOperation(checkInfo); | ||
227 | } | ||
228 | } | ||
229 | } | ||
230 | |||
231 | |||
232 | private void categorizeExtend(PConstraintInfo constraintInfo) { | ||
233 | PConstraintCategory category = constraintInfo.getCategory(pBody, boundVariables); | ||
234 | if (category == PConstraintCategory.PRESENT) { | ||
235 | presentExtends.add(constraintInfo); | ||
236 | } else { | ||
237 | // do not categorize past/future operations | ||
238 | } | ||
239 | } | ||
240 | |||
241 | |||
242 | public PBody getAssociatedPBody() { | ||
243 | return pBody; | ||
244 | } | ||
245 | |||
246 | public List<PConstraintInfo> getOperations() { | ||
247 | return operationsList; | ||
248 | } | ||
249 | |||
250 | public Set<PVariable> getBoundVariables() { | ||
251 | return boundVariables; | ||
252 | } | ||
253 | |||
254 | /** | ||
255 | * @return the derived cost of the plan contained in the state | ||
256 | */ | ||
257 | public double getCost() { | ||
258 | return cost; | ||
259 | } | ||
260 | |||
261 | |||
262 | /** | ||
263 | * @return cumulative branching factor | ||
264 | * @since 2.1 | ||
265 | */ | ||
266 | public double getCummulativeProduct() { | ||
267 | return cummulativeProduct; | ||
268 | } | ||
269 | |||
270 | public List<PConstraintInfo> getPresentExtends() { | ||
271 | return presentExtends; | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * Contains only those variables that are added by the newest extend | ||
276 | * (or the initially bound ones if no extend yet) | ||
277 | */ | ||
278 | public Collection<PVariable> getDeltaVariables() { | ||
279 | return deltaVariables; | ||
280 | } | ||
281 | |||
282 | |||
283 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/AbstractOperationCompiler.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/AbstractOperationCompiler.java new file mode 100644 index 00000000..aaa6414c --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/AbstractOperationCompiler.java | |||
@@ -0,0 +1,430 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.planner.compiler; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.HashMap; | ||
13 | import java.util.HashSet; | ||
14 | import java.util.List; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | import java.util.stream.Collectors; | ||
18 | import java.util.stream.Stream; | ||
19 | |||
20 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
21 | import tools.refinery.interpreter.localsearch.operations.util.CallInformation; | ||
22 | import tools.refinery.interpreter.localsearch.matcher.CallWithAdornment; | ||
23 | import tools.refinery.interpreter.localsearch.operations.check.AggregatorCheck; | ||
24 | import tools.refinery.interpreter.localsearch.operations.check.BinaryTransitiveClosureCheck; | ||
25 | import tools.refinery.interpreter.localsearch.operations.check.CheckConstant; | ||
26 | import tools.refinery.interpreter.localsearch.operations.check.CheckPositivePatternCall; | ||
27 | import tools.refinery.interpreter.localsearch.operations.check.CountCheck; | ||
28 | import tools.refinery.interpreter.localsearch.operations.check.ExpressionCheck; | ||
29 | import tools.refinery.interpreter.localsearch.operations.check.ExpressionEvalCheck; | ||
30 | import tools.refinery.interpreter.localsearch.operations.check.InequalityCheck; | ||
31 | import tools.refinery.interpreter.localsearch.operations.check.NACOperation; | ||
32 | import tools.refinery.interpreter.localsearch.operations.extend.AggregatorExtend; | ||
33 | import tools.refinery.interpreter.localsearch.operations.extend.CountOperation; | ||
34 | import tools.refinery.interpreter.localsearch.operations.extend.ExpressionEval; | ||
35 | import tools.refinery.interpreter.localsearch.operations.extend.ExtendBinaryTransitiveClosure; | ||
36 | import tools.refinery.interpreter.localsearch.operations.extend.ExtendConstant; | ||
37 | import tools.refinery.interpreter.localsearch.operations.extend.ExtendPositivePatternCall; | ||
38 | import tools.refinery.interpreter.localsearch.planner.util.CompilerHelper; | ||
39 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
40 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
41 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
42 | import tools.refinery.interpreter.matchers.planning.QueryProcessingException; | ||
43 | import tools.refinery.interpreter.matchers.planning.SubPlan; | ||
44 | import tools.refinery.interpreter.matchers.planning.operations.PApply; | ||
45 | import tools.refinery.interpreter.matchers.planning.operations.POperation; | ||
46 | import tools.refinery.interpreter.matchers.planning.operations.PProject; | ||
47 | import tools.refinery.interpreter.matchers.planning.operations.PStart; | ||
48 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
49 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
50 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
51 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.ExportedParameter; | ||
52 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
53 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.Inequality; | ||
54 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.NegativePatternCall; | ||
55 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.PatternMatchCounter; | ||
56 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
57 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure; | ||
58 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
59 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.ConstantValue; | ||
60 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
61 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.TypeConstraint; | ||
62 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
63 | |||
64 | /** | ||
65 | * @author Zoltan Ujhelyi | ||
66 | * @since 1.7 | ||
67 | * | ||
68 | */ | ||
69 | public abstract class AbstractOperationCompiler implements IOperationCompiler { | ||
70 | |||
71 | protected static final String UNSUPPORTED_TYPE_MESSAGE = "Unsupported type: "; | ||
72 | |||
73 | protected abstract void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping); | ||
74 | |||
75 | /** | ||
76 | * @throws InterpreterRuntimeException | ||
77 | */ | ||
78 | protected abstract void createCheck(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping); | ||
79 | |||
80 | /** | ||
81 | * @throws InterpreterRuntimeException | ||
82 | */ | ||
83 | protected abstract void createCheck(TypeFilterConstraint typeConstraint, Map<PVariable, Integer> variableMapping); | ||
84 | |||
85 | /** | ||
86 | * @since 2.0 | ||
87 | * @throws InterpreterRuntimeException | ||
88 | */ | ||
89 | protected abstract void createUnaryTypeCheck(IInputKey type, int position); | ||
90 | |||
91 | protected List<ISearchOperation> operations; | ||
92 | protected Set<CallWithAdornment> dependencies = new HashSet<>(); | ||
93 | protected Map<PConstraint, Set<Integer>> variableBindings; | ||
94 | private Map<PVariable, Integer> variableMappings; | ||
95 | protected final IQueryRuntimeContext runtimeContext; | ||
96 | |||
97 | public AbstractOperationCompiler(IQueryRuntimeContext runtimeContext) { | ||
98 | this.runtimeContext = runtimeContext; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * Compiles a plan of <code>POperation</code>s to a list of type <code>List<ISearchOperation></code> | ||
103 | * | ||
104 | * @param plan | ||
105 | * @param boundParameters | ||
106 | * @return an ordered list of POperations that make up the compiled search plan | ||
107 | * @throws InterpreterRuntimeException | ||
108 | */ | ||
109 | @Override | ||
110 | public List<ISearchOperation> compile(SubPlan plan, Set<PParameter> boundParameters) { | ||
111 | |||
112 | variableMappings = CompilerHelper.createVariableMapping(plan); | ||
113 | variableBindings = CompilerHelper.cacheVariableBindings(plan, variableMappings, boundParameters); | ||
114 | |||
115 | operations = new ArrayList<>(); | ||
116 | |||
117 | List<POperation> operationList = CompilerHelper.createOperationsList(plan); | ||
118 | for (POperation pOperation : operationList) { | ||
119 | compile(pOperation, variableMappings); | ||
120 | } | ||
121 | |||
122 | return operations; | ||
123 | } | ||
124 | |||
125 | private void compile(POperation pOperation, Map<PVariable, Integer> variableMapping) { | ||
126 | |||
127 | if (pOperation instanceof PApply) { | ||
128 | PApply pApply = (PApply) pOperation; | ||
129 | PConstraint pConstraint = pApply.getPConstraint(); | ||
130 | |||
131 | if (isCheck(pConstraint, variableMapping)) { | ||
132 | // check | ||
133 | createCheckDispatcher(pConstraint, variableMapping); | ||
134 | } else { | ||
135 | // extend | ||
136 | createExtendDispatcher(pConstraint, variableMapping); | ||
137 | } | ||
138 | |||
139 | } else if (pOperation instanceof PStart) { | ||
140 | // nop | ||
141 | } else if (pOperation instanceof PProject) { | ||
142 | // nop | ||
143 | } else { | ||
144 | throw new QueryProcessingException("PStart, PApply or PProject was expected, received: " + pOperation.getClass(), null,"Unexpected POperation type", null); | ||
145 | } | ||
146 | |||
147 | } | ||
148 | |||
149 | private void createCheckDispatcher(PConstraint pConstraint, Map<PVariable, Integer> variableMapping) { | ||
150 | |||
151 | |||
152 | // DeferredPConstraint subclasses | ||
153 | |||
154 | // Equalities are normalized | ||
155 | |||
156 | if (pConstraint instanceof Inequality) { | ||
157 | createCheck((Inequality) pConstraint, variableMapping); | ||
158 | } else if (pConstraint instanceof PositivePatternCall){ | ||
159 | createCheck((PositivePatternCall) pConstraint, variableMapping); | ||
160 | } else if (pConstraint instanceof NegativePatternCall) { | ||
161 | createCheck((NegativePatternCall) pConstraint,variableMapping); | ||
162 | } else if (pConstraint instanceof AggregatorConstraint) { | ||
163 | createCheck((AggregatorConstraint) pConstraint, variableMapping); | ||
164 | } else if (pConstraint instanceof PatternMatchCounter) { | ||
165 | createCheck((PatternMatchCounter) pConstraint, variableMapping); | ||
166 | } else if (pConstraint instanceof ExpressionEvaluation) { | ||
167 | createCheck((ExpressionEvaluation) pConstraint, variableMapping); | ||
168 | } else if (pConstraint instanceof TypeFilterConstraint) { | ||
169 | createCheck((TypeFilterConstraint) pConstraint,variableMapping); | ||
170 | } else if (pConstraint instanceof ExportedParameter) { | ||
171 | // Nothing to do here | ||
172 | } else | ||
173 | |||
174 | // EnumerablePConstraint subclasses | ||
175 | |||
176 | if (pConstraint instanceof BinaryTransitiveClosure) { | ||
177 | createCheck((BinaryTransitiveClosure) pConstraint, variableMapping); | ||
178 | } else if (pConstraint instanceof BinaryReflexiveTransitiveClosure) { | ||
179 | createCheck((BinaryReflexiveTransitiveClosure)pConstraint, variableMapping); | ||
180 | } else if (pConstraint instanceof ConstantValue) { | ||
181 | createCheck((ConstantValue) pConstraint, variableMapping); | ||
182 | } else if (pConstraint instanceof TypeConstraint) { | ||
183 | createCheck((TypeConstraint) pConstraint,variableMapping); | ||
184 | } else { | ||
185 | String msg = "Unsupported Check constraint: "+pConstraint.toString(); | ||
186 | throw new QueryProcessingException(msg, null, msg, null); | ||
187 | } | ||
188 | |||
189 | } | ||
190 | |||
191 | protected void createExtendDispatcher(PConstraint pConstraint, Map<PVariable, Integer> variableMapping) { | ||
192 | |||
193 | // DeferredPConstraint subclasses | ||
194 | |||
195 | // Equalities are normalized | ||
196 | if (pConstraint instanceof PositivePatternCall) { | ||
197 | createExtend((PositivePatternCall)pConstraint, variableMapping); | ||
198 | } else if (pConstraint instanceof AggregatorConstraint) { | ||
199 | createExtend((AggregatorConstraint) pConstraint, variableMapping); | ||
200 | } else if (pConstraint instanceof PatternMatchCounter) { | ||
201 | createExtend((PatternMatchCounter) pConstraint, variableMapping); | ||
202 | } else if (pConstraint instanceof ExpressionEvaluation) { | ||
203 | createExtend((ExpressionEvaluation) pConstraint, variableMapping); | ||
204 | } else if (pConstraint instanceof ExportedParameter) { | ||
205 | // ExportedParameters are compiled to NOP | ||
206 | } else | ||
207 | |||
208 | // EnumerablePConstraint subclasses | ||
209 | |||
210 | if (pConstraint instanceof ConstantValue) { | ||
211 | createExtend((ConstantValue) pConstraint, variableMapping); | ||
212 | } else if (pConstraint instanceof TypeConstraint) { | ||
213 | createExtend((TypeConstraint) pConstraint, variableMapping); | ||
214 | } else if (pConstraint instanceof BinaryTransitiveClosure) { | ||
215 | createExtend((BinaryTransitiveClosure)pConstraint, variableMapping); | ||
216 | } else if (pConstraint instanceof BinaryReflexiveTransitiveClosure) { | ||
217 | createExtend((BinaryReflexiveTransitiveClosure)pConstraint, variableMapping); | ||
218 | } else { | ||
219 | String msg = "Unsupported Extend constraint: "+pConstraint.toString(); | ||
220 | throw new QueryProcessingException(msg, null, msg, null); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | private boolean isCheck(PConstraint pConstraint, final Map<PVariable, Integer> variableMapping) { | ||
225 | if (pConstraint instanceof NegativePatternCall){ | ||
226 | return true; | ||
227 | }else if (pConstraint instanceof PositivePatternCall){ | ||
228 | // Positive pattern call is check if all non-single used variables are bound | ||
229 | List<Integer> callVariables = pConstraint.getAffectedVariables().stream() | ||
230 | .filter(input -> input.getReferringConstraints().size() > 1) | ||
231 | .map(variableMapping::get) | ||
232 | .collect(Collectors.toList()); | ||
233 | return variableBindings.get(pConstraint).containsAll(callVariables); | ||
234 | }else if (pConstraint instanceof AggregatorConstraint){ | ||
235 | PVariable outputvar = ((AggregatorConstraint) pConstraint).getResultVariable(); | ||
236 | return variableBindings.get(pConstraint).contains(variableMapping.get(outputvar)); | ||
237 | }else if (pConstraint instanceof PatternMatchCounter){ | ||
238 | PVariable outputvar = ((PatternMatchCounter) pConstraint).getResultVariable(); | ||
239 | return variableBindings.get(pConstraint).contains(variableMapping.get(outputvar)); | ||
240 | }else if (pConstraint instanceof ExpressionEvaluation){ | ||
241 | PVariable outputvar = ((ExpressionEvaluation) pConstraint).getOutputVariable(); | ||
242 | return outputvar == null || variableBindings.get(pConstraint).contains(variableMapping.get(outputvar)); | ||
243 | } else { | ||
244 | // In other cases, all variables shall be bound to be a check | ||
245 | Set<PVariable> affectedVariables = pConstraint.getAffectedVariables(); | ||
246 | Set<Integer> varIndices = new HashSet<>(); | ||
247 | for (PVariable variable : affectedVariables) { | ||
248 | varIndices.add(variableMapping.get(variable)); | ||
249 | } | ||
250 | return variableBindings.get(pConstraint).containsAll(varIndices); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | @Override | ||
255 | public Set<CallWithAdornment> getDependencies() { | ||
256 | return dependencies; | ||
257 | } | ||
258 | |||
259 | /** | ||
260 | * @return the cached variable bindings for the previously created plan | ||
261 | */ | ||
262 | @Override | ||
263 | public Map<PVariable, Integer> getVariableMappings() { | ||
264 | return variableMappings; | ||
265 | } | ||
266 | |||
267 | protected void createCheck(PatternMatchCounter counter, Map<PVariable, Integer> variableMapping) { | ||
268 | CallInformation information = CallInformation.create(counter, variableMapping, variableBindings.get(counter)); | ||
269 | operations.add(new CountCheck(information, variableMapping.get(counter.getResultVariable()))); | ||
270 | dependencies.add(information.getCallWithAdornment()); | ||
271 | } | ||
272 | |||
273 | protected void createCheck(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) { | ||
274 | CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall)); | ||
275 | operations.add(new CheckPositivePatternCall(information)); | ||
276 | dependencies.add(information.getCallWithAdornment()); | ||
277 | } | ||
278 | |||
279 | protected void createCheck(ConstantValue constant, Map<PVariable, Integer> variableMapping) { | ||
280 | int position = variableMapping.get(constant.getVariablesTuple().get(0)); | ||
281 | operations.add(new CheckConstant(position, constant.getSupplierKey())); | ||
282 | } | ||
283 | |||
284 | protected void createCheck(BinaryTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) { | ||
285 | int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0)); | ||
286 | int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1)); | ||
287 | |||
288 | //The second parameter is NOT bound during execution! | ||
289 | CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, Stream.of(sourcePosition).collect(Collectors.toSet())); | ||
290 | operations.add(new BinaryTransitiveClosureCheck(information, sourcePosition, targetPosition, false)); | ||
291 | dependencies.add(information.getCallWithAdornment()); | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * @since 2.0 | ||
296 | */ | ||
297 | protected void createCheck(BinaryReflexiveTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) { | ||
298 | int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0)); | ||
299 | int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1)); | ||
300 | |||
301 | //The second parameter is NOT bound during execution! | ||
302 | CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, Stream.of(sourcePosition).collect(Collectors.toSet())); | ||
303 | createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), sourcePosition); | ||
304 | operations.add(new BinaryTransitiveClosureCheck(information, sourcePosition, targetPosition, true)); | ||
305 | dependencies.add(information.getCallWithAdornment()); | ||
306 | } | ||
307 | |||
308 | protected void createCheck(ExpressionEvaluation expressionEvaluation, Map<PVariable, Integer> variableMapping) { | ||
309 | // Fill unbound variables with null; simply copy all variables. Unbound variables will be null anyway | ||
310 | Iterable<String> inputParameterNames = expressionEvaluation.getEvaluator().getInputParameterNames(); | ||
311 | Map<String, Integer> nameMap = new HashMap<>(); | ||
312 | |||
313 | for (String pVariableName : inputParameterNames) { | ||
314 | PVariable pVariable = expressionEvaluation.getPSystem().getVariableByNameChecked(pVariableName); | ||
315 | nameMap.put(pVariableName, variableMapping.get(pVariable)); | ||
316 | } | ||
317 | |||
318 | // output variable can be null; if null it is an ExpressionCheck | ||
319 | if(expressionEvaluation.getOutputVariable() == null){ | ||
320 | operations.add(new ExpressionCheck(expressionEvaluation.getEvaluator(), nameMap)); | ||
321 | } else { | ||
322 | operations.add(new ExpressionEvalCheck(expressionEvaluation.getEvaluator(), nameMap, expressionEvaluation.isUnwinding(), variableMapping.get(expressionEvaluation.getOutputVariable()))); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | protected void createCheck(AggregatorConstraint aggregator, Map<PVariable, Integer> variableMapping) { | ||
327 | CallInformation information = CallInformation.create(aggregator, variableMapping, variableBindings.get(aggregator)); | ||
328 | operations.add(new AggregatorCheck(information, aggregator, variableMapping.get(aggregator.getResultVariable()))); | ||
329 | dependencies.add(information.getCallWithAdornment()); | ||
330 | } | ||
331 | |||
332 | protected void createCheck(NegativePatternCall negativePatternCall, Map<PVariable, Integer> variableMapping) { | ||
333 | CallInformation information = CallInformation.create(negativePatternCall, variableMapping, variableBindings.get(negativePatternCall)); | ||
334 | operations.add(new NACOperation(information)); | ||
335 | dependencies.add(information.getCallWithAdornment()); | ||
336 | } | ||
337 | |||
338 | protected void createCheck(Inequality inequality, Map<PVariable, Integer> variableMapping) { | ||
339 | operations.add(new InequalityCheck(variableMapping.get(inequality.getWho()), variableMapping.get(inequality.getWithWhom()))); | ||
340 | } | ||
341 | |||
342 | protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) { | ||
343 | CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall)); | ||
344 | operations.add(new ExtendPositivePatternCall(information)); | ||
345 | dependencies.add(information.getCallWithAdornment()); | ||
346 | } | ||
347 | |||
348 | protected void createExtend(BinaryTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) { | ||
349 | int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0)); | ||
350 | int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1)); | ||
351 | |||
352 | boolean sourceBound = variableBindings.get(binaryTransitiveClosure).contains(sourcePosition); | ||
353 | boolean targetBound = variableBindings.get(binaryTransitiveClosure).contains(targetPosition); | ||
354 | |||
355 | CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, variableBindings.get(binaryTransitiveClosure)); | ||
356 | |||
357 | if (sourceBound && !targetBound) { | ||
358 | operations.add(new ExtendBinaryTransitiveClosure.Forward(information, sourcePosition, targetPosition, false)); | ||
359 | dependencies.add(information.getCallWithAdornment()); | ||
360 | } else if (!sourceBound && targetBound) { | ||
361 | operations.add(new ExtendBinaryTransitiveClosure.Backward(information, sourcePosition, targetPosition, false)); | ||
362 | dependencies.add(information.getCallWithAdornment()); | ||
363 | } else { | ||
364 | String msg = "Binary transitive closure not supported with two unbound parameters"; | ||
365 | throw new QueryProcessingException(msg, null, msg, binaryTransitiveClosure.getPSystem().getPattern()); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * @since 2.0 | ||
371 | */ | ||
372 | protected void createExtend(BinaryReflexiveTransitiveClosure binaryTransitiveClosure, Map<PVariable, Integer> variableMapping) { | ||
373 | int sourcePosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(0)); | ||
374 | int targetPosition = variableMapping.get(binaryTransitiveClosure.getVariablesTuple().get(1)); | ||
375 | |||
376 | boolean sourceBound = variableBindings.get(binaryTransitiveClosure).contains(sourcePosition); | ||
377 | boolean targetBound = variableBindings.get(binaryTransitiveClosure).contains(targetPosition); | ||
378 | |||
379 | CallInformation information = CallInformation.create(binaryTransitiveClosure, variableMapping, variableBindings.get(binaryTransitiveClosure)); | ||
380 | |||
381 | if (sourceBound && !targetBound) { | ||
382 | createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), sourcePosition); | ||
383 | operations.add(new ExtendBinaryTransitiveClosure.Forward(information, sourcePosition, targetPosition, true)); | ||
384 | dependencies.add(information.getCallWithAdornment()); | ||
385 | } else if (!sourceBound && targetBound) { | ||
386 | createUnaryTypeCheck(binaryTransitiveClosure.getUniverseType(), targetPosition); | ||
387 | operations.add(new ExtendBinaryTransitiveClosure.Backward(information, sourcePosition, targetPosition, true)); | ||
388 | dependencies.add(information.getCallWithAdornment()); | ||
389 | } else { | ||
390 | String msg = "Binary reflective transitive closure not supported with two unbound parameters"; | ||
391 | throw new QueryProcessingException(msg, null, msg, binaryTransitiveClosure.getPSystem().getPattern()); | ||
392 | } | ||
393 | } | ||
394 | |||
395 | protected void createExtend(ConstantValue constant, Map<PVariable, Integer> variableMapping) { | ||
396 | int position = variableMapping.get(constant.getVariablesTuple().get(0)); | ||
397 | operations.add(new ExtendConstant(position, constant.getSupplierKey())); | ||
398 | } | ||
399 | |||
400 | protected void createExtend(ExpressionEvaluation expressionEvaluation, Map<PVariable, Integer> variableMapping) { | ||
401 | // Fill unbound variables with null; simply copy all variables. Unbound variables will be null anyway | ||
402 | Iterable<String> inputParameterNames = expressionEvaluation.getEvaluator().getInputParameterNames(); | ||
403 | Map<String, Integer> nameMap = new HashMap<>(); | ||
404 | |||
405 | for (String pVariableName : inputParameterNames) { | ||
406 | PVariable pVariable = expressionEvaluation.getPSystem().getVariableByNameChecked(pVariableName); | ||
407 | nameMap.put(pVariableName, variableMapping.get(pVariable)); | ||
408 | } | ||
409 | |||
410 | // output variable can be null; if null it is an ExpressionCheck | ||
411 | if(expressionEvaluation.getOutputVariable() == null){ | ||
412 | operations.add(new ExpressionCheck(expressionEvaluation.getEvaluator(), nameMap)); | ||
413 | } else { | ||
414 | operations.add(new ExpressionEval(expressionEvaluation.getEvaluator(), nameMap, expressionEvaluation.isUnwinding(), variableMapping.get(expressionEvaluation.getOutputVariable()))); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | protected void createExtend(AggregatorConstraint aggregator, Map<PVariable, Integer> variableMapping) { | ||
419 | CallInformation information = CallInformation.create(aggregator, variableMapping, variableBindings.get(aggregator)); | ||
420 | operations.add(new AggregatorExtend(information, aggregator, variableMapping.get(aggregator.getResultVariable()))); | ||
421 | dependencies.add(information.getCallWithAdornment()); | ||
422 | } | ||
423 | |||
424 | protected void createExtend(PatternMatchCounter patternMatchCounter, Map<PVariable, Integer> variableMapping) { | ||
425 | CallInformation information = CallInformation.create(patternMatchCounter, variableMapping, variableBindings.get(patternMatchCounter)); | ||
426 | operations.add(new CountOperation(information, variableMapping.get(patternMatchCounter.getResultVariable()))); | ||
427 | dependencies.add(information.getCallWithAdornment()); | ||
428 | } | ||
429 | |||
430 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/GenericOperationCompiler.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/GenericOperationCompiler.java new file mode 100644 index 00000000..82e63ba8 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/GenericOperationCompiler.java | |||
@@ -0,0 +1,101 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
3 | * Copyright (c) 2023 The Refinery Authors <https://refinery.tools/> | ||
4 | * This program and the accompanying materials are made available under the | ||
5 | * terms of the Eclipse Public License v. 2.0 which is available at | ||
6 | * http://www.eclipse.org/legal/epl-v20.html. | ||
7 | * | ||
8 | * SPDX-License-Identifier: EPL-2.0 | ||
9 | *******************************************************************************/ | ||
10 | package tools.refinery.interpreter.localsearch.planner.compiler; | ||
11 | |||
12 | import tools.refinery.interpreter.localsearch.operations.generic.GenericTypeCheck; | ||
13 | import tools.refinery.interpreter.localsearch.operations.generic.GenericTypeExtend; | ||
14 | import tools.refinery.interpreter.localsearch.operations.generic.GenericTypeExtendSingleValue; | ||
15 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
16 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
17 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
18 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
19 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.TypeConstraint; | ||
20 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
21 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
22 | |||
23 | import java.util.*; | ||
24 | |||
25 | /** | ||
26 | * @author Zoltan Ujhelyi | ||
27 | * @since 1.7 | ||
28 | * | ||
29 | */ | ||
30 | public class GenericOperationCompiler extends AbstractOperationCompiler { | ||
31 | |||
32 | public GenericOperationCompiler(IQueryRuntimeContext runtimeContext) { | ||
33 | super(runtimeContext); | ||
34 | } | ||
35 | |||
36 | @Override | ||
37 | protected void createCheck(TypeFilterConstraint typeConstraint, Map<PVariable, Integer> variableMapping) { | ||
38 | IInputKey inputKey = typeConstraint.getInputKey(); | ||
39 | Tuple tuple = typeConstraint.getVariablesTuple(); | ||
40 | int[] positions = new int[tuple.getSize()]; | ||
41 | for (int i = 0; i < tuple.getSize(); i++) { | ||
42 | PVariable variable = (PVariable) tuple.get(i); | ||
43 | positions[i] = variableMapping.get(variable); | ||
44 | } | ||
45 | operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(variableMapping.size(), positions))); | ||
46 | |||
47 | } | ||
48 | |||
49 | @Override | ||
50 | protected void createCheck(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) { | ||
51 | IInputKey inputKey = typeConstraint.getSupplierKey(); | ||
52 | Tuple tuple = typeConstraint.getVariablesTuple(); | ||
53 | int[] positions = new int[tuple.getSize()]; | ||
54 | for (int i = 0; i < tuple.getSize(); i++) { | ||
55 | PVariable variable = (PVariable) tuple.get(i); | ||
56 | positions[i] = variableMapping.get(variable); | ||
57 | } | ||
58 | operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(variableMapping.size(), positions))); | ||
59 | } | ||
60 | |||
61 | @Override | ||
62 | protected void createUnaryTypeCheck(IInputKey inputKey, int position) { | ||
63 | int[] positions = new int[] {position}; | ||
64 | operations.add(new GenericTypeCheck(inputKey, positions, TupleMask.fromSelectedIndices(1, positions))); | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | protected void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) { | ||
69 | IInputKey inputKey = typeConstraint.getSupplierKey(); | ||
70 | Tuple tuple = typeConstraint.getVariablesTuple(); | ||
71 | |||
72 | int[] positions = new int[tuple.getSize()]; | ||
73 | List<Integer> boundVariableIndices = new ArrayList<>(); | ||
74 | List<Integer> boundVariables = new ArrayList<>(); | ||
75 | Set<Integer> unboundVariables = new HashSet<>(); | ||
76 | for (int i = 0; i < tuple.getSize(); i++) { | ||
77 | PVariable variable = (PVariable) tuple.get(i); | ||
78 | Integer position = variableMapping.get(variable); | ||
79 | positions[i] = position; | ||
80 | if (variableBindings.get(typeConstraint).contains(position)) { | ||
81 | boundVariableIndices.add(i); | ||
82 | boundVariables.add(position); | ||
83 | } else { | ||
84 | unboundVariables.add(position); | ||
85 | } | ||
86 | } | ||
87 | TupleMask indexerMask = TupleMask.fromSelectedIndices(inputKey.getArity(), boundVariableIndices); | ||
88 | TupleMask callMask = TupleMask.fromSelectedIndices(variableMapping.size(), boundVariables); | ||
89 | // If multiple tuple elements from the indexer should be bound to the same variable, we must use a | ||
90 | // {@link GenericTypeExtend} check whether the tuple elements have the same value. | ||
91 | if (unboundVariables.size() == 1 && indexerMask.getSize() + 1 == indexerMask.getSourceWidth()) { | ||
92 | operations.add(new GenericTypeExtendSingleValue(inputKey, positions, callMask, indexerMask, unboundVariables.iterator().next())); | ||
93 | } else { | ||
94 | operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables)); | ||
95 | } | ||
96 | |||
97 | } | ||
98 | |||
99 | |||
100 | |||
101 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/IOperationCompiler.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/IOperationCompiler.java new file mode 100644 index 00000000..aee26cc9 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/compiler/IOperationCompiler.java | |||
@@ -0,0 +1,53 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs 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.interpreter.localsearch.planner.compiler; | ||
10 | |||
11 | import java.util.List; | ||
12 | import java.util.Map; | ||
13 | import java.util.Set; | ||
14 | |||
15 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
16 | import tools.refinery.interpreter.localsearch.matcher.CallWithAdornment; | ||
17 | import tools.refinery.interpreter.localsearch.matcher.MatcherReference; | ||
18 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
19 | import tools.refinery.interpreter.matchers.planning.SubPlan; | ||
20 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
21 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
22 | |||
23 | /** | ||
24 | * An operation compiler is responsible for creating executable search plans from the subplan structure. | ||
25 | * | ||
26 | * @author Zoltan Ujhelyi | ||
27 | * @since 1.7 | ||
28 | * | ||
29 | */ | ||
30 | public interface IOperationCompiler { | ||
31 | |||
32 | /** | ||
33 | * Compiles a plan of <code>POperation</code>s to a list of type <code>List<ISearchOperation></code> | ||
34 | * | ||
35 | * @param plan | ||
36 | * @param boundParameters | ||
37 | * @return an ordered list of POperations that make up the compiled search plan | ||
38 | * @throws InterpreterRuntimeException | ||
39 | */ | ||
40 | List<ISearchOperation> compile(SubPlan plan, Set<PParameter> boundParameters); | ||
41 | |||
42 | /** | ||
43 | * Replaces previous method returning {@link MatcherReference} | ||
44 | * @since 2.1 | ||
45 | */ | ||
46 | Set<CallWithAdornment> getDependencies(); | ||
47 | |||
48 | /** | ||
49 | * @return the cached variable bindings for the previously created plan | ||
50 | */ | ||
51 | Map<PVariable, Integer> getVariableMappings(); | ||
52 | |||
53 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/IConstraintEvaluationContext.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/IConstraintEvaluationContext.java new file mode 100644 index 00000000..11195fff --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/IConstraintEvaluationContext.java | |||
@@ -0,0 +1,63 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.planner.cost; | ||
10 | |||
11 | import tools.refinery.interpreter.matchers.backend.ResultProviderRequestor; | ||
12 | import tools.refinery.interpreter.matchers.context.IQueryResultProviderAccess; | ||
13 | import tools.refinery.interpreter.matchers.context.IQueryRuntimeContext; | ||
14 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
15 | import java.util.Collection; | ||
16 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
17 | import tools.refinery.interpreter.matchers.psystem.analysis.QueryAnalyzer; | ||
18 | |||
19 | /** | ||
20 | * This interface denotes the evaluation context of a constraint, intended for cost estimation. Provides access to information | ||
21 | * on which the cost function can base its calculation. | ||
22 | * | ||
23 | * @author Grill Balázs | ||
24 | * @since 1.4 | ||
25 | * @noimplement | ||
26 | */ | ||
27 | public interface IConstraintEvaluationContext { | ||
28 | |||
29 | /** | ||
30 | * Get the constraint to be evaluated | ||
31 | */ | ||
32 | public PConstraint getConstraint(); | ||
33 | |||
34 | /** | ||
35 | * Unbound variables at the time of evaluating the constraint | ||
36 | */ | ||
37 | public Collection<PVariable> getFreeVariables(); | ||
38 | |||
39 | /** | ||
40 | * Bound variables at the time of evaluating the constraint | ||
41 | */ | ||
42 | public Collection<PVariable> getBoundVariables(); | ||
43 | |||
44 | public IQueryRuntimeContext getRuntimeContext(); | ||
45 | |||
46 | /** | ||
47 | * @since 1.5 | ||
48 | */ | ||
49 | public QueryAnalyzer getQueryAnalyzer(); | ||
50 | |||
51 | /** | ||
52 | * @deprecated use {@link #resultProviderRequestor()} | ||
53 | * @since 1.5 | ||
54 | */ | ||
55 | @Deprecated | ||
56 | public IQueryResultProviderAccess resultProviderAccess(); | ||
57 | |||
58 | /** | ||
59 | * @since 2.1 | ||
60 | */ | ||
61 | public ResultProviderRequestor resultProviderRequestor(); | ||
62 | |||
63 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/ICostFunction.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/ICostFunction.java new file mode 100644 index 00000000..717c7787 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/ICostFunction.java | |||
@@ -0,0 +1,22 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.planner.cost; | ||
10 | |||
11 | /** | ||
12 | * Common interface for cost function implementation | ||
13 | * | ||
14 | * @author Grill Balázs | ||
15 | * @since 1.4 | ||
16 | * | ||
17 | */ | ||
18 | public interface ICostFunction{ | ||
19 | |||
20 | public double apply(IConstraintEvaluationContext input); | ||
21 | |||
22 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java new file mode 100644 index 00000000..cb859f62 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/HybridMatcherConstraintCostFunction.java | |||
@@ -0,0 +1,92 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.planner.cost.impl; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.Set; | ||
15 | import java.util.stream.Collectors; | ||
16 | |||
17 | import tools.refinery.interpreter.localsearch.planner.cost.IConstraintEvaluationContext; | ||
18 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
19 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
20 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
21 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.ConstantValue; | ||
22 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
23 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
24 | import tools.refinery.interpreter.matchers.tuple.Tuple; | ||
25 | import tools.refinery.interpreter.matchers.tuple.Tuples; | ||
26 | |||
27 | /** | ||
28 | * This cost function is intended to be used on hybrid configuration, with the strict restriction than any | ||
29 | * non-flattened positive pattern call is executed with Rete engine. This implementation provides the exact number | ||
30 | * of matches by invoking the result provider for the called pattern. | ||
31 | * | ||
32 | * @deprecated {@link StatisticsBasedConstraintCostFunction} should use {@link IQueryResultProvider#estimateCardinality(TupleMask, org.eclipse.viatra.query.runtime.matchers.util.Accuracy)} | ||
33 | */ | ||
34 | @Deprecated | ||
35 | public class HybridMatcherConstraintCostFunction extends IndexerBasedConstraintCostFunction { | ||
36 | |||
37 | @Override | ||
38 | protected double _calculateCost(PositivePatternCall patternCall, IConstraintEvaluationContext context) { | ||
39 | // Determine local constant constraints which is used to filter results | ||
40 | Tuple variables = patternCall.getVariablesTuple(); | ||
41 | Set<Object> variablesSet = variables.getDistinctElements(); | ||
42 | final Map<PVariable, Object> constantMap = new HashMap<>(); | ||
43 | for (PConstraint _constraint : patternCall.getPSystem().getConstraints()) { | ||
44 | if (_constraint instanceof ConstantValue){ | ||
45 | ConstantValue constraint = (ConstantValue) _constraint; | ||
46 | PVariable variable = (PVariable) constraint.getVariablesTuple().get(0); | ||
47 | if (variablesSet.contains(variable) && context.getBoundVariables().contains(variable)) { | ||
48 | constantMap.put(variable, constraint.getSupplierKey()); | ||
49 | } | ||
50 | } | ||
51 | } | ||
52 | |||
53 | // Determine filter | ||
54 | Object[] filter = new Object[variables.getSize()]; | ||
55 | for(int i=0; i < variables.getSize(); i++){ | ||
56 | filter[i] = constantMap.get(variables.get(i)); | ||
57 | } | ||
58 | |||
59 | // aggregate keys are the bound and not filtered variables | ||
60 | // These will be fixed in runtime, but unknown at planning time | ||
61 | // This is represented by indices to ease working with result tuples | ||
62 | final Map<Object, Integer> variableIndices = variables.invertIndex(); | ||
63 | List<Integer> aggregateKeys = context.getBoundVariables().stream() | ||
64 | .filter(input -> !constantMap.containsKey(input)) | ||
65 | .map(variableIndices::get) | ||
66 | .collect(Collectors.toList()); | ||
67 | |||
68 | IQueryResultProvider resultProvider = context.resultProviderRequestor().requestResultProvider(patternCall, null); | ||
69 | Map<Tuple, Integer> aggregatedCounts = new HashMap<>(); | ||
70 | |||
71 | // Iterate over all matches and count together matches that has equal values on | ||
72 | // aggregateKeys positions. The cost of the pattern call is considered to be the | ||
73 | // Maximum of these counted values | ||
74 | |||
75 | int result = 0; | ||
76 | // NOTE: a stream is not an iterable (cannot be iterated more than once), so to use it in a for-loop | ||
77 | // it has to be wrapped; in the following line a lambda is used to implement Iterable#iterator() | ||
78 | for (Tuple match : (Iterable<Tuple>) () -> resultProvider.getAllMatches(filter).iterator()) { | ||
79 | Tuple extracted = Tuples.flatTupleOf(aggregateKeys.stream().map(match::get).toArray()); | ||
80 | int count = (aggregatedCounts.containsKey(extracted)) | ||
81 | ? aggregatedCounts.get(extracted) + 1 | ||
82 | : 1; | ||
83 | aggregatedCounts.put(extracted, count); | ||
84 | if (result < count) { | ||
85 | result = count; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | return result; | ||
90 | } | ||
91 | |||
92 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java new file mode 100644 index 00000000..33d34ed8 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/IndexerBasedConstraintCostFunction.java | |||
@@ -0,0 +1,49 @@ | |||
1 | /** | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.planner.cost.impl; | ||
10 | |||
11 | import java.util.Optional; | ||
12 | |||
13 | import tools.refinery.interpreter.localsearch.planner.cost.IConstraintEvaluationContext; | ||
14 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
15 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
16 | import tools.refinery.interpreter.matchers.util.Accuracy; | ||
17 | |||
18 | /** | ||
19 | * Cost function which calculates cost based on the cardinality of items in the runtime model, provided by the base indexer | ||
20 | * | ||
21 | * @author Grill Balázs | ||
22 | * @since 1.4 | ||
23 | */ | ||
24 | public class IndexerBasedConstraintCostFunction extends StatisticsBasedConstraintCostFunction { | ||
25 | |||
26 | |||
27 | |||
28 | |||
29 | /** | ||
30 | * | ||
31 | */ | ||
32 | public IndexerBasedConstraintCostFunction() { | ||
33 | super(); | ||
34 | } | ||
35 | |||
36 | /** | ||
37 | * @param inverseNavigationPenalty | ||
38 | * @since 2.1 | ||
39 | */ | ||
40 | public IndexerBasedConstraintCostFunction(double inverseNavigationPenalty) { | ||
41 | super(inverseNavigationPenalty); | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public Optional<Long> projectionSize(IConstraintEvaluationContext input, IInputKey supplierKey, TupleMask groupMask, Accuracy requiredAccuracy) { | ||
46 | return input.getRuntimeContext().estimateCardinality(supplierKey, groupMask, requiredAccuracy); | ||
47 | } | ||
48 | |||
49 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java new file mode 100644 index 00000000..200b2902 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/StatisticsBasedConstraintCostFunction.java | |||
@@ -0,0 +1,413 @@ | |||
1 | /** | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs 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.interpreter.localsearch.planner.cost.impl; | ||
10 | |||
11 | import static tools.refinery.interpreter.matchers.planning.helpers.StatisticsHelper.min; | ||
12 | |||
13 | import java.util.ArrayList; | ||
14 | import java.util.Arrays; | ||
15 | import java.util.Collection; | ||
16 | import java.util.Collections; | ||
17 | import java.util.List; | ||
18 | import java.util.Map; | ||
19 | import java.util.Optional; | ||
20 | import java.util.Set; | ||
21 | |||
22 | import tools.refinery.interpreter.localsearch.matcher.integration.AbstractLocalSearchResultProvider; | ||
23 | import tools.refinery.interpreter.localsearch.planner.cost.IConstraintEvaluationContext; | ||
24 | import tools.refinery.interpreter.localsearch.planner.cost.ICostFunction; | ||
25 | import tools.refinery.interpreter.matchers.InterpreterRuntimeException; | ||
26 | import tools.refinery.interpreter.matchers.backend.IQueryResultProvider; | ||
27 | import tools.refinery.interpreter.matchers.context.IInputKey; | ||
28 | import tools.refinery.interpreter.matchers.planning.helpers.FunctionalDependencyHelper; | ||
29 | import tools.refinery.interpreter.matchers.psystem.IQueryReference; | ||
30 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
31 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
32 | import tools.refinery.interpreter.matchers.psystem.analysis.QueryAnalyzer; | ||
33 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
34 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.ExportedParameter; | ||
35 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.ExpressionEvaluation; | ||
36 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.Inequality; | ||
37 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.NegativePatternCall; | ||
38 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.PatternMatchCounter; | ||
39 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.TypeFilterConstraint; | ||
40 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.BinaryReflexiveTransitiveClosure; | ||
41 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
42 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.ConstantValue; | ||
43 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.PositivePatternCall; | ||
44 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.TypeConstraint; | ||
45 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
46 | import tools.refinery.interpreter.matchers.tuple.TupleMask; | ||
47 | import tools.refinery.interpreter.matchers.util.Accuracy; | ||
48 | import tools.refinery.interpreter.matchers.util.Preconditions; | ||
49 | |||
50 | /** | ||
51 | * Cost function which calculates cost based on the cardinality of items in the runtime model | ||
52 | * | ||
53 | * <p> To provide custom statistics, override | ||
54 | * {@link #projectionSize(IConstraintEvaluationContext, IInputKey, TupleMask, Accuracy)} | ||
55 | * and {@link #bucketSize(IQueryReference, IConstraintEvaluationContext, TupleMask)}. | ||
56 | * | ||
57 | * @author Grill Balázs | ||
58 | * @since 1.4 | ||
59 | */ | ||
60 | public abstract class StatisticsBasedConstraintCostFunction implements ICostFunction { | ||
61 | protected static final double MAX_COST = 250.0; | ||
62 | |||
63 | protected static final double DEFAULT_COST = StatisticsBasedConstraintCostFunction.MAX_COST - 100.0; | ||
64 | |||
65 | /** | ||
66 | * @since 2.1 | ||
67 | */ | ||
68 | public static final double INVERSE_NAVIGATION_PENALTY_DEFAULT = 0.10; | ||
69 | /** | ||
70 | * @since 2.1 | ||
71 | */ | ||
72 | public static final double INVERSE_NAVIGATION_PENALTY_GENERIC = 0.01; | ||
73 | /** | ||
74 | * @since 2.7 | ||
75 | */ | ||
76 | public static final double EVAL_UNWIND_EXTENSION_FACTOR = 3.0; | ||
77 | |||
78 | private final double inverseNavigationPenalty; | ||
79 | |||
80 | |||
81 | /** | ||
82 | * @since 2.1 | ||
83 | */ | ||
84 | public StatisticsBasedConstraintCostFunction(double inverseNavigationPenalty) { | ||
85 | super(); | ||
86 | this.inverseNavigationPenalty = inverseNavigationPenalty; | ||
87 | } | ||
88 | public StatisticsBasedConstraintCostFunction() { | ||
89 | this(INVERSE_NAVIGATION_PENALTY_DEFAULT); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * @deprecated call and implement {@link #projectionSize(IConstraintEvaluationContext, IInputKey, TupleMask, Accuracy)} instead | ||
94 | */ | ||
95 | @Deprecated | ||
96 | public long countTuples(final IConstraintEvaluationContext input, final IInputKey supplierKey) { | ||
97 | return projectionSize(input, supplierKey, TupleMask.identity(supplierKey.getArity()), Accuracy.EXACT_COUNT).orElse(-1L); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Override this to provide custom statistics on edge/node counts. | ||
102 | * New implementors shall implement this instead of {@link #countTuples(IConstraintEvaluationContext, IInputKey)} | ||
103 | * @since 2.1 | ||
104 | */ | ||
105 | public Optional<Long> projectionSize(final IConstraintEvaluationContext input, final IInputKey supplierKey, | ||
106 | final TupleMask groupMask, Accuracy requiredAccuracy) { | ||
107 | long legacyCount = countTuples(input, supplierKey); | ||
108 | return legacyCount < 0 ? Optional.empty() : Optional.of(legacyCount); | ||
109 | } | ||
110 | |||
111 | /** | ||
112 | * Override this to provide custom estimates for match set sizes of called patterns. | ||
113 | * @since 2.1 | ||
114 | */ | ||
115 | public Optional<Double> bucketSize(final IQueryReference patternCall, | ||
116 | final IConstraintEvaluationContext input, TupleMask projMask) { | ||
117 | IQueryResultProvider resultProvider = input.resultProviderRequestor().requestResultProvider(patternCall, null); | ||
118 | // TODO hack: use LS cost instead of true bucket size estimate | ||
119 | if (resultProvider instanceof AbstractLocalSearchResultProvider) { | ||
120 | double estimatedCost = ((AbstractLocalSearchResultProvider) resultProvider).estimateCost(projMask); | ||
121 | return Optional.of(estimatedCost); | ||
122 | } else { | ||
123 | return resultProvider.estimateAverageBucketSize(projMask, Accuracy.APPROXIMATION); | ||
124 | } | ||
125 | } | ||
126 | |||
127 | |||
128 | |||
129 | @Override | ||
130 | public double apply(final IConstraintEvaluationContext input) { | ||
131 | return this.calculateCost(input.getConstraint(), input); | ||
132 | } | ||
133 | |||
134 | protected double _calculateCost(final ConstantValue constant, final IConstraintEvaluationContext input) { | ||
135 | return 0.0f; | ||
136 | } | ||
137 | |||
138 | protected double _calculateCost(final TypeConstraint constraint, final IConstraintEvaluationContext input) { | ||
139 | final Collection<PVariable> freeMaskVariables = input.getFreeVariables(); | ||
140 | final Collection<PVariable> boundMaskVariables = input.getBoundVariables(); | ||
141 | IInputKey supplierKey = constraint.getSupplierKey(); | ||
142 | long arity = supplierKey.getArity(); | ||
143 | |||
144 | if ((arity == 1)) { | ||
145 | // unary constraint | ||
146 | return calculateUnaryConstraintCost(constraint, input); | ||
147 | } else if ((arity == 2)) { | ||
148 | // binary constraint | ||
149 | PVariable srcVariable = ((PVariable) constraint.getVariablesTuple().get(0)); | ||
150 | PVariable dstVariable = ((PVariable) constraint.getVariablesTuple().get(1)); | ||
151 | boolean isInverse = false; | ||
152 | // Check if inverse navigation is needed along the edge | ||
153 | if ((freeMaskVariables.contains(srcVariable) && boundMaskVariables.contains(dstVariable))) { | ||
154 | isInverse = true; | ||
155 | } | ||
156 | double binaryExtendCost = calculateBinaryCost(supplierKey, srcVariable, dstVariable, isInverse, input); | ||
157 | // Make inverse navigation slightly more expensive than forward navigation | ||
158 | // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=501078 | ||
159 | return (isInverse) ? binaryExtendCost + inverseNavigationPenalty : binaryExtendCost; | ||
160 | } else { | ||
161 | // n-ary constraint | ||
162 | throw new UnsupportedOperationException("Cost calculation for arity " + arity + " is not implemented yet"); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | |||
167 | /** | ||
168 | * @deprecated use/implement {@link #calculateBinaryCost(IInputKey, PVariable, PVariable, boolean, IConstraintEvaluationContext)} instead | ||
169 | */ | ||
170 | @Deprecated | ||
171 | protected double calculateBinaryExtendCost(final IInputKey supplierKey, final PVariable srcVariable, | ||
172 | final PVariable dstVariable, final boolean isInverse, long edgeCount /* TODO remove */, | ||
173 | final IConstraintEvaluationContext input) { | ||
174 | throw new UnsupportedOperationException(); | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * @since 2.1 | ||
179 | */ | ||
180 | protected double calculateBinaryCost(final IInputKey supplierKey, final PVariable srcVariable, | ||
181 | final PVariable dstVariable, final boolean isInverse, | ||
182 | final IConstraintEvaluationContext input) { | ||
183 | final Collection<PVariable> freeMaskVariables = input.getFreeVariables(); | ||
184 | final PConstraint constraint = input.getConstraint(); | ||
185 | |||
186 | // IQueryMetaContext metaContext = input.getRuntimeContext().getMetaContext(); | ||
187 | // Collection<InputKeyImplication> implications = metaContext.getImplications(supplierKey); | ||
188 | |||
189 | Optional<Long> edgeUpper = projectionSize(input, supplierKey, TupleMask.identity(2), Accuracy.BEST_UPPER_BOUND); | ||
190 | Optional<Long> srcUpper = projectionSize(input, supplierKey, TupleMask.selectSingle(0, 2), Accuracy.BEST_UPPER_BOUND); | ||
191 | Optional<Long> dstUpper = projectionSize(input, supplierKey, TupleMask.selectSingle(1, 2), Accuracy.BEST_UPPER_BOUND); | ||
192 | |||
193 | if (freeMaskVariables.contains(srcVariable) && freeMaskVariables.contains(dstVariable)) { | ||
194 | Double branchCount = edgeUpper.map(Long::doubleValue).orElse( | ||
195 | srcUpper.map(Long::doubleValue).orElse(DEFAULT_COST) | ||
196 | * | ||
197 | dstUpper.map(Long::doubleValue).orElse(DEFAULT_COST) | ||
198 | ); | ||
199 | return branchCount; | ||
200 | |||
201 | } else { | ||
202 | |||
203 | Optional<Long> srcLower = projectionSize(input, supplierKey, TupleMask.selectSingle(0, 2), Accuracy.APPROXIMATION); | ||
204 | Optional<Long> dstLower = projectionSize(input, supplierKey, TupleMask.selectSingle(1, 2), Accuracy.APPROXIMATION); | ||
205 | |||
206 | List<Optional<Long>> nodeLower = Arrays.asList(srcLower, dstLower); | ||
207 | List<Optional<Long>> nodeUpper = Arrays.asList(srcUpper, dstUpper); | ||
208 | |||
209 | int from = isInverse ? 1 : 0; | ||
210 | int to = isInverse ? 0 : 1; | ||
211 | |||
212 | Optional<Double> costEstimate = Optional.empty(); | ||
213 | |||
214 | if (!freeMaskVariables.contains(srcVariable) && !freeMaskVariables.contains(dstVariable)) { | ||
215 | // both variables bound, this is a simple check | ||
216 | costEstimate = min(costEstimate, 0.9); | ||
217 | } // TODO use bucket size estimation in the runtime context | ||
218 | costEstimate = min(costEstimate, | ||
219 | edgeUpper.flatMap(edges -> | ||
220 | nodeLower.get(from).map(fromNodes -> | ||
221 | // amortize edges over start nodes | ||
222 | (fromNodes == 0) ? 0.0 : (((double) edges) / fromNodes) | ||
223 | ))); | ||
224 | if (navigatesThroughFunctionalDependencyInverse(input, constraint)) { | ||
225 | costEstimate = min(costEstimate, nodeUpper.get(to).flatMap(toNodes -> | ||
226 | nodeLower.get(from).map(fromNodes -> | ||
227 | // due to a reverse functional dependency, the destination count is an upper bound for the edge count | ||
228 | (fromNodes == 0) ? 0.0 : ((double) toNodes) / fromNodes | ||
229 | ))); | ||
230 | } | ||
231 | if (! edgeUpper.isPresent()) { | ||
232 | costEstimate = min(costEstimate, nodeUpper.get(to).flatMap(toNodes -> | ||
233 | nodeLower.get(from).map(fromNodes -> | ||
234 | // If count is 0, no such element exists in the model, so there will be no branching | ||
235 | // TODO rethink, why dstNodeCount / srcNodeCount instead of dstNodeCount? | ||
236 | // The universally valid bound would be something like sparseEdgeEstimate = dstNodeCount + 1.0 | ||
237 | // If we assume sparseness, we can reduce it by a SPARSENESS_FACTOR (e.g. 0.1). | ||
238 | // Alternatively, discount dstNodeCount * srcNodeCount on a SPARSENESS_EXPONENT (e.g 0.75) and then amortize over srcNodeCount. | ||
239 | fromNodes != 0 ? Math.max(1.0, ((double) toNodes) / fromNodes) : 1.0 | ||
240 | ))); | ||
241 | } | ||
242 | if (navigatesThroughFunctionalDependency(input, constraint)) { | ||
243 | // At most one destination value | ||
244 | costEstimate = min(costEstimate, 1.0); | ||
245 | } | ||
246 | |||
247 | return costEstimate.orElse(DEFAULT_COST); | ||
248 | |||
249 | } | ||
250 | } | ||
251 | |||
252 | /** | ||
253 | * @since 1.7 | ||
254 | */ | ||
255 | protected boolean navigatesThroughFunctionalDependency(final IConstraintEvaluationContext input, | ||
256 | final PConstraint constraint) { | ||
257 | return navigatesThroughFunctionalDependency(input, constraint, input.getBoundVariables(), input.getFreeVariables()); | ||
258 | } | ||
259 | /** | ||
260 | * @since 2.1 | ||
261 | */ | ||
262 | protected boolean navigatesThroughFunctionalDependencyInverse(final IConstraintEvaluationContext input, | ||
263 | final PConstraint constraint) { | ||
264 | return navigatesThroughFunctionalDependency(input, constraint, input.getFreeVariables(), input.getBoundVariables()); | ||
265 | } | ||
266 | /** | ||
267 | * @since 2.1 | ||
268 | */ | ||
269 | protected boolean navigatesThroughFunctionalDependency(final IConstraintEvaluationContext input, | ||
270 | final PConstraint constraint, Collection<PVariable> determining, Collection<PVariable> determined) { | ||
271 | final QueryAnalyzer queryAnalyzer = input.getQueryAnalyzer(); | ||
272 | final Map<Set<PVariable>, Set<PVariable>> functionalDependencies = queryAnalyzer | ||
273 | .getFunctionalDependencies(Collections.singleton(constraint), false); | ||
274 | final Set<PVariable> impliedVariables = FunctionalDependencyHelper.closureOf(determining, | ||
275 | functionalDependencies); | ||
276 | return ((impliedVariables != null) && impliedVariables.containsAll(determined)); | ||
277 | } | ||
278 | |||
279 | protected double calculateUnaryConstraintCost(final TypeConstraint constraint, | ||
280 | final IConstraintEvaluationContext input) { | ||
281 | PVariable variable = (PVariable) constraint.getVariablesTuple().get(0); | ||
282 | if (input.getBoundVariables().contains(variable)) { | ||
283 | return 0.9; | ||
284 | } else { | ||
285 | return projectionSize(input, constraint.getSupplierKey(), TupleMask.identity(1), Accuracy.APPROXIMATION) | ||
286 | .map(count -> 1.0 + count).orElse(DEFAULT_COST); | ||
287 | } | ||
288 | } | ||
289 | |||
290 | protected double _calculateCost(final ExportedParameter exportedParam, final IConstraintEvaluationContext input) { | ||
291 | return 0.0; | ||
292 | } | ||
293 | |||
294 | protected double _calculateCost(final TypeFilterConstraint exportedParam, | ||
295 | final IConstraintEvaluationContext input) { | ||
296 | return 0.0; | ||
297 | } | ||
298 | |||
299 | protected double _calculateCost(final PositivePatternCall patternCall, final IConstraintEvaluationContext input) { | ||
300 | final List<Integer> boundPositions = new ArrayList<>(); | ||
301 | final List<PParameter> parameters = patternCall.getReferredQuery().getParameters(); | ||
302 | for (int i = 0; (i < parameters.size()); i++) { | ||
303 | final PVariable variable = patternCall.getVariableInTuple(i); | ||
304 | if (input.getBoundVariables().contains(variable)) boundPositions.add(i); | ||
305 | } | ||
306 | TupleMask projMask = TupleMask.fromSelectedIndices(parameters.size(), boundPositions); | ||
307 | |||
308 | return bucketSize(patternCall, input, projMask).orElse(DEFAULT_COST); | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * @since 1.7 | ||
314 | */ | ||
315 | protected double _calculateCost(final ExpressionEvaluation evaluation, final IConstraintEvaluationContext input) { | ||
316 | // Even if there are multiple results here, if all output variable is bound eval unwind will not result in | ||
317 | // multiple branches in search graph | ||
318 | final double multiplier = evaluation.isUnwinding() && !input.getFreeVariables().isEmpty() | ||
319 | ? EVAL_UNWIND_EXTENSION_FACTOR | ||
320 | : 1.0; | ||
321 | return _calculateCost((PConstraint) evaluation, input) * multiplier; | ||
322 | } | ||
323 | |||
324 | /** | ||
325 | * @since 1.7 | ||
326 | */ | ||
327 | protected double _calculateCost(final Inequality inequality, final IConstraintEvaluationContext input) { | ||
328 | return _calculateCost((PConstraint)inequality, input); | ||
329 | } | ||
330 | |||
331 | /** | ||
332 | * @since 1.7 | ||
333 | */ | ||
334 | protected double _calculateCost(final AggregatorConstraint aggregator, final IConstraintEvaluationContext input) { | ||
335 | return _calculateCost((PConstraint)aggregator, input); | ||
336 | } | ||
337 | |||
338 | /** | ||
339 | * @since 1.7 | ||
340 | */ | ||
341 | protected double _calculateCost(final NegativePatternCall call, final IConstraintEvaluationContext input) { | ||
342 | return _calculateCost((PConstraint)call, input); | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * @since 1.7 | ||
347 | */ | ||
348 | protected double _calculateCost(final PatternMatchCounter counter, final IConstraintEvaluationContext input) { | ||
349 | return _calculateCost((PConstraint)counter, input); | ||
350 | } | ||
351 | |||
352 | /** | ||
353 | * @since 1.7 | ||
354 | */ | ||
355 | protected double _calculateCost(final BinaryTransitiveClosure closure, final IConstraintEvaluationContext input) { | ||
356 | // if (input.getFreeVariables().size() == 1) return 3.0; | ||
357 | return StatisticsBasedConstraintCostFunction.DEFAULT_COST; | ||
358 | } | ||
359 | |||
360 | /** | ||
361 | * @since 2.0 | ||
362 | */ | ||
363 | protected double _calculateCost(final BinaryReflexiveTransitiveClosure closure, final IConstraintEvaluationContext input) { | ||
364 | // if (input.getFreeVariables().size() == 1) return 3.0; | ||
365 | return StatisticsBasedConstraintCostFunction.DEFAULT_COST; | ||
366 | } | ||
367 | |||
368 | /** | ||
369 | * Default cost calculation strategy | ||
370 | */ | ||
371 | protected double _calculateCost(final PConstraint constraint, final IConstraintEvaluationContext input) { | ||
372 | if (input.getFreeVariables().isEmpty()) { | ||
373 | return 1.0; | ||
374 | } else { | ||
375 | return StatisticsBasedConstraintCostFunction.DEFAULT_COST; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /** | ||
380 | * @throws InterpreterRuntimeException | ||
381 | */ | ||
382 | public double calculateCost(final PConstraint constraint, final IConstraintEvaluationContext input) { | ||
383 | Preconditions.checkArgument(constraint != null, "Set constraint value correctly"); | ||
384 | if (constraint instanceof ExportedParameter) { | ||
385 | return _calculateCost((ExportedParameter) constraint, input); | ||
386 | } else if (constraint instanceof TypeFilterConstraint) { | ||
387 | return _calculateCost((TypeFilterConstraint) constraint, input); | ||
388 | } else if (constraint instanceof ConstantValue) { | ||
389 | return _calculateCost((ConstantValue) constraint, input); | ||
390 | } else if (constraint instanceof PositivePatternCall) { | ||
391 | return _calculateCost((PositivePatternCall) constraint, input); | ||
392 | } else if (constraint instanceof TypeConstraint) { | ||
393 | return _calculateCost((TypeConstraint) constraint, input); | ||
394 | } else if (constraint instanceof ExpressionEvaluation) { | ||
395 | return _calculateCost((ExpressionEvaluation) constraint, input); | ||
396 | } else if (constraint instanceof Inequality) { | ||
397 | return _calculateCost((Inequality) constraint, input); | ||
398 | } else if (constraint instanceof AggregatorConstraint) { | ||
399 | return _calculateCost((AggregatorConstraint) constraint, input); | ||
400 | } else if (constraint instanceof NegativePatternCall) { | ||
401 | return _calculateCost((NegativePatternCall) constraint, input); | ||
402 | } else if (constraint instanceof PatternMatchCounter) { | ||
403 | return _calculateCost((PatternMatchCounter) constraint, input); | ||
404 | } else if (constraint instanceof BinaryTransitiveClosure) { | ||
405 | return _calculateCost((BinaryTransitiveClosure) constraint, input); | ||
406 | } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { | ||
407 | return _calculateCost((BinaryReflexiveTransitiveClosure) constraint, input); | ||
408 | } else { | ||
409 | // Default cost calculation | ||
410 | return _calculateCost(constraint, input); | ||
411 | } | ||
412 | } | ||
413 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java new file mode 100644 index 00000000..50168130 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/cost/impl/VariableBindingBasedCostFunction.java | |||
@@ -0,0 +1,95 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Balazs Grill, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.planner.cost.impl; | ||
10 | |||
11 | import java.util.Set; | ||
12 | |||
13 | import tools.refinery.interpreter.localsearch.planner.cost.IConstraintEvaluationContext; | ||
14 | import tools.refinery.interpreter.localsearch.planner.cost.ICostFunction; | ||
15 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
16 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
17 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.AggregatorConstraint; | ||
18 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.ExportedParameter; | ||
19 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.NegativePatternCall; | ||
20 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.BinaryTransitiveClosure; | ||
21 | import tools.refinery.interpreter.matchers.psystem.basicenumerables.ConstantValue; | ||
22 | |||
23 | /** | ||
24 | * This class can be used to calculate cost of application of a constraint with a given adornment. | ||
25 | * | ||
26 | * For now the logic is based on the following principles: | ||
27 | * | ||
28 | * <li>The transitive closures, NACs and count finds are the most expensive operations | ||
29 | * | ||
30 | * <li>The number of free variables increase the cost | ||
31 | * | ||
32 | * <li>If all the variables of a constraint are free, then its cost equals to twice the number of its parameter | ||
33 | * variables. This solves the problem of unnecessary iteration over instances at the beginning of a plan (thus causing | ||
34 | * very long run times when executing the plan) by applying constraints based on structural features as soon as | ||
35 | * possible. | ||
36 | * | ||
37 | * <br> | ||
38 | * | ||
39 | * @author Marton Bur | ||
40 | * @since 1.4 | ||
41 | * | ||
42 | */ | ||
43 | public class VariableBindingBasedCostFunction implements ICostFunction { | ||
44 | |||
45 | // Static cost definitions | ||
46 | private static int MAX = 1000; | ||
47 | private static int exportedParameterCost = MAX - 20; | ||
48 | private static int binaryTransitiveClosureCost = MAX - 50; | ||
49 | private static int nacCost = MAX - 100; | ||
50 | private static int aggregatorCost = MAX - 200; | ||
51 | private static int constantCost = 0; | ||
52 | |||
53 | @Override | ||
54 | public double apply(IConstraintEvaluationContext input) { | ||
55 | PConstraint constraint = input.getConstraint(); | ||
56 | Set<PVariable> affectedVariables = constraint.getAffectedVariables(); | ||
57 | |||
58 | int cost = 0; | ||
59 | |||
60 | // For constants the cost is determined to be 0.0 | ||
61 | // The following constraints should be checks: | ||
62 | // * Binary transitive closure | ||
63 | // * NAC | ||
64 | // * count | ||
65 | // * exported parameter - only a metadata | ||
66 | if (constraint instanceof ConstantValue) { | ||
67 | cost = constantCost; | ||
68 | } else if (constraint instanceof BinaryTransitiveClosure) { | ||
69 | cost = binaryTransitiveClosureCost; | ||
70 | } else if (constraint instanceof NegativePatternCall) { | ||
71 | cost = nacCost; | ||
72 | } else if (constraint instanceof AggregatorConstraint) { | ||
73 | cost = aggregatorCost; | ||
74 | } else if (constraint instanceof ExportedParameter) { | ||
75 | cost = exportedParameterCost; | ||
76 | } else { | ||
77 | // In case of other constraints count the number of unbound variables | ||
78 | for (PVariable pVariable : affectedVariables) { | ||
79 | if (input.getFreeVariables().contains(pVariable)) { | ||
80 | // For each free variable ('without-value-variable') increase cost | ||
81 | cost += 1; | ||
82 | } | ||
83 | } | ||
84 | if (cost == affectedVariables.size()) { | ||
85 | // If all the variables are free, double the cost. | ||
86 | // This ensures that iteration costs more | ||
87 | cost *= 2; | ||
88 | } | ||
89 | |||
90 | } | ||
91 | |||
92 | return Float.valueOf(cost); | ||
93 | } | ||
94 | |||
95 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/util/CompilerHelper.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/util/CompilerHelper.java new file mode 100644 index 00000000..3e2e2a60 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/util/CompilerHelper.java | |||
@@ -0,0 +1,209 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2014, Marton Bur, Daniel Segesdi, Akos Horvath, Zoltan Ujhelyi, 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.interpreter.localsearch.planner.util; | ||
10 | |||
11 | import java.util.ArrayList; | ||
12 | import java.util.Collections; | ||
13 | import java.util.HashMap; | ||
14 | import java.util.HashSet; | ||
15 | import java.util.List; | ||
16 | import java.util.Map; | ||
17 | import java.util.Set; | ||
18 | |||
19 | import tools.refinery.interpreter.matchers.planning.SubPlan; | ||
20 | import tools.refinery.interpreter.matchers.planning.operations.PApply; | ||
21 | import tools.refinery.interpreter.matchers.planning.operations.POperation; | ||
22 | import tools.refinery.interpreter.matchers.psystem.PConstraint; | ||
23 | import tools.refinery.interpreter.matchers.psystem.PVariable; | ||
24 | import tools.refinery.interpreter.matchers.psystem.basicdeferred.ExportedParameter; | ||
25 | import tools.refinery.interpreter.matchers.psystem.queries.PParameter; | ||
26 | |||
27 | /** | ||
28 | * | ||
29 | * Helper methods for compiling SubPlans | ||
30 | * | ||
31 | * @author Marton Bur | ||
32 | * | ||
33 | */ | ||
34 | public class CompilerHelper { | ||
35 | |||
36 | private CompilerHelper() {/*Utility class constructor*/} | ||
37 | |||
38 | private static boolean isUserSpecified(PVariable var) { | ||
39 | return !var.isVirtual() && !var.getName().startsWith("_"); | ||
40 | } | ||
41 | |||
42 | public static Map<PVariable, Integer> createVariableMapping(SubPlan plan) { | ||
43 | Map<PVariable, Integer> variableMapping = new HashMap<>(); | ||
44 | |||
45 | int variableNumber = 0; | ||
46 | |||
47 | // Important note: this list might contain duplications when parameters are made equal inside the pattern | ||
48 | // This is the expected and normal behavior | ||
49 | List<PVariable> symbolicParameterVariables = plan.getBody().getSymbolicParameterVariables(); | ||
50 | for (PVariable pVariable : symbolicParameterVariables) { | ||
51 | if (!variableMapping.containsKey(pVariable)) { | ||
52 | variableMapping.put(pVariable, variableNumber++); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | List<PVariable> allVariables = new ArrayList<>(plan.getBody().getUniqueVariables()); | ||
57 | Collections.sort(allVariables, (left, right) -> { | ||
58 | boolean leftUserSpecified = isUserSpecified(left); | ||
59 | boolean rightUserSpecified = isUserSpecified(right); | ||
60 | if (leftUserSpecified && !rightUserSpecified) { | ||
61 | return -1; | ||
62 | } else if (!leftUserSpecified && rightUserSpecified) { | ||
63 | return +1; | ||
64 | } else { | ||
65 | return left.getName().compareTo(right.getName()); | ||
66 | } | ||
67 | }); | ||
68 | for (PVariable pVariable : allVariables) { | ||
69 | if (!variableMapping.containsKey(pVariable)) { | ||
70 | variableMapping.put(pVariable, variableNumber++); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return variableMapping; | ||
75 | } | ||
76 | |||
77 | public static Map<PConstraint, Set<Integer>> cacheVariableBindings(SubPlan plan, | ||
78 | Map<PVariable, Integer> variableMappings, Set<PParameter> adornment) { | ||
79 | |||
80 | Set<Integer> externallyBoundVariables = getVariableIndicesForParameters(plan, variableMappings, | ||
81 | adornment); | ||
82 | |||
83 | Map<PConstraint, Set<Integer>> variableBindings = new HashMap<>(); | ||
84 | |||
85 | List<SubPlan> allPlansInHierarchy = getAllParentPlans(plan); | ||
86 | for (SubPlan subPlan : allPlansInHierarchy) { | ||
87 | POperation operation = subPlan.getOperation(); | ||
88 | |||
89 | if (operation instanceof PApply) { | ||
90 | PConstraint pConstraint = ((PApply) operation).getPConstraint(); | ||
91 | Set<Integer> boundVariableIndices = getParametersBoundByParentPlan(variableMappings, subPlan); | ||
92 | boundVariableIndices.addAll(externallyBoundVariables); | ||
93 | |||
94 | variableBindings.put(pConstraint, boundVariableIndices); | ||
95 | } | ||
96 | } | ||
97 | return variableBindings; | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Returns the list of variable indexes that are bound by the parent plan. | ||
102 | */ | ||
103 | private static Set<Integer> getParametersBoundByParentPlan(Map<PVariable, Integer> variableMappings, | ||
104 | SubPlan subPlan) { | ||
105 | if (!subPlan.getParentPlans().isEmpty()) { | ||
106 | SubPlan parentPlan = subPlan.getParentPlans().get(0); | ||
107 | Set<PConstraint> enforcedConstraints = parentPlan.getAllEnforcedConstraints(); | ||
108 | Set<PVariable> affectedVariables = getAffectedVariables(enforcedConstraints); | ||
109 | return getVariableIndices(variableMappings, affectedVariables); | ||
110 | } | ||
111 | return Collections.emptySet(); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * @param plan | ||
116 | * @return all the ancestor plans including the given plan | ||
117 | */ | ||
118 | private static List<SubPlan> getAllParentPlans(SubPlan plan) { | ||
119 | SubPlan currentPlan = plan; | ||
120 | List<SubPlan> allPlans = new ArrayList<>(); | ||
121 | allPlans.add(plan); | ||
122 | while (!currentPlan.getParentPlans().isEmpty()) { | ||
123 | // In the local search it is assumed that only a single parent exists | ||
124 | currentPlan = currentPlan.getParentPlans().get(0); | ||
125 | allPlans.add(currentPlan); | ||
126 | } | ||
127 | |||
128 | return allPlans; | ||
129 | } | ||
130 | |||
131 | /** | ||
132 | * @param variableMappings | ||
133 | * the mapping between variables and their indices | ||
134 | * @param variables | ||
135 | * variables to get the indices for | ||
136 | * @return the set of variable indices for the given variables | ||
137 | */ | ||
138 | private static Set<Integer> getVariableIndices(Map<PVariable, Integer> variableMappings, | ||
139 | Iterable<PVariable> variables) { | ||
140 | Set<Integer> variableIndices = new HashSet<>(); | ||
141 | for (PVariable pVariable : variables) { | ||
142 | variableIndices.add(variableMappings.get(pVariable)); | ||
143 | } | ||
144 | return variableIndices; | ||
145 | } | ||
146 | |||
147 | /** | ||
148 | * Returns all affected variables of the given PConstraints. | ||
149 | */ | ||
150 | private static Set<PVariable> getAffectedVariables(Set<PConstraint> pConstraints) { | ||
151 | Set<PVariable> allDeducedVariables = new HashSet<>(); | ||
152 | for (PConstraint pConstraint : pConstraints) { | ||
153 | allDeducedVariables.addAll(pConstraint.getAffectedVariables()); | ||
154 | } | ||
155 | return allDeducedVariables; | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * Transforms the index of a parameter into the index of a variable of the normalized body. | ||
160 | * | ||
161 | * @param plan | ||
162 | * the SubPlan containing the original body and its parameters | ||
163 | * @param variableMappings | ||
164 | * the mapping of PVariables to their indices | ||
165 | * @param parameters | ||
166 | * a set of parameters | ||
167 | * @return the index of the variable corresponding to the parameter at the given index | ||
168 | */ | ||
169 | private static Set<Integer> getVariableIndicesForParameters(SubPlan plan, | ||
170 | Map<PVariable, Integer> variableMappings, Set<PParameter> parameters) { | ||
171 | Map<PParameter, PVariable> parameterMapping = new HashMap<>(); | ||
172 | for (ExportedParameter constraint : plan.getBody().getSymbolicParameters()) { | ||
173 | parameterMapping.put(constraint.getPatternParameter(), constraint.getParameterVariable()); | ||
174 | } | ||
175 | |||
176 | Set<Integer> variableIndices = new HashSet<>(); | ||
177 | for (PParameter parameter : parameters) { | ||
178 | PVariable parameterVariable = parameterMapping.get(parameter); | ||
179 | if (parameterVariable == null) { | ||
180 | // XXX In case of older (pre-1.4) VIATRA versions, PParameters were not stable, see bug 498348 | ||
181 | parameterVariable = plan.getBody().getVariableByNameChecked(parameter.getName()); | ||
182 | } | ||
183 | Integer variableIndex = variableMappings.get(parameterVariable); | ||
184 | variableIndices.add(variableIndex); | ||
185 | } | ||
186 | return variableIndices; | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * Extracts the operations from a SubPlan into a list of POperations in the order of execution | ||
191 | * | ||
192 | * @param plan | ||
193 | * the SubPlan from wich the POperations should be extracted | ||
194 | * @return list of POperations extracted from the <code>plan</code> | ||
195 | */ | ||
196 | public static List<POperation> createOperationsList(SubPlan plan) { | ||
197 | List<POperation> operationsList = new ArrayList<>(); | ||
198 | while (plan.getParentPlans().size() > 0) { | ||
199 | operationsList.add(plan.getOperation()); | ||
200 | SubPlan parentPlan = plan.getParentPlans().get(0); | ||
201 | plan = parentPlan; | ||
202 | } | ||
203 | operationsList.add(plan.getOperation()); | ||
204 | |||
205 | Collections.reverse(operationsList); | ||
206 | return operationsList; | ||
207 | } | ||
208 | |||
209 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/util/OperationCostComparator.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/util/OperationCostComparator.java new file mode 100644 index 00000000..27cf79f2 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/planner/util/OperationCostComparator.java | |||
@@ -0,0 +1,26 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Danil 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.interpreter.localsearch.planner.util; | ||
10 | |||
11 | import java.util.Comparator; | ||
12 | |||
13 | import tools.refinery.interpreter.localsearch.planner.PConstraintInfo; | ||
14 | |||
15 | /** | ||
16 | * @author Marton Bur | ||
17 | * | ||
18 | */ | ||
19 | public class OperationCostComparator implements Comparator<PConstraintInfo>{ | ||
20 | |||
21 | @Override | ||
22 | public int compare(PConstraintInfo o1, PConstraintInfo o2) { | ||
23 | return Double.compare(o1.getCost(), o2.getCost()); | ||
24 | } | ||
25 | |||
26 | } | ||
diff --git a/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/profiler/LocalSearchProfilerAdapter.java b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/profiler/LocalSearchProfilerAdapter.java new file mode 100644 index 00000000..a866f976 --- /dev/null +++ b/subprojects/interpreter-localsearch/src/main/java/tools/refinery/interpreter/localsearch/profiler/LocalSearchProfilerAdapter.java | |||
@@ -0,0 +1,83 @@ | |||
1 | /******************************************************************************* | ||
2 | * Copyright (c) 2010-2016, Grill Balázs, 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.interpreter.localsearch.profiler; | ||
10 | |||
11 | import java.util.HashMap; | ||
12 | import java.util.List; | ||
13 | import java.util.Map; | ||
14 | import java.util.stream.Collectors; | ||
15 | |||
16 | import tools.refinery.interpreter.localsearch.operations.ISearchOperation; | ||
17 | import tools.refinery.interpreter.localsearch.MatchingFrame; | ||
18 | import tools.refinery.interpreter.localsearch.matcher.ILocalSearchAdapter; | ||
19 | import tools.refinery.interpreter.localsearch.matcher.LocalSearchMatcher; | ||
20 | import tools.refinery.interpreter.localsearch.matcher.MatcherReference; | ||
21 | import tools.refinery.interpreter.localsearch.plan.SearchPlan; | ||
22 | import tools.refinery.interpreter.localsearch.plan.SearchPlanExecutor; | ||
23 | |||
24 | /** | ||
25 | * This is a simple {@link ILocalSearchAdapter} which capable of counting | ||
26 | * each search operation execution then printing it in human readably form | ||
27 | * (along with the executed plans) using {@link #toString()} | ||
28 | * @author Grill Balázs | ||
29 | * @since 1.5 | ||
30 | * | ||
31 | */ | ||
32 | public class LocalSearchProfilerAdapter implements ILocalSearchAdapter { | ||
33 | |||
34 | private final Map<MatcherReference, List<SearchPlan>> planReference = new HashMap<>(); | ||
35 | |||
36 | private final Map<ISearchOperation, Integer> successfulOperationCounts = new HashMap<>(); | ||
37 | private final Map<ISearchOperation, Integer> failedOperationCounts = new HashMap<>(); | ||
38 | |||
39 | @Override | ||
40 | public void patternMatchingStarted(LocalSearchMatcher lsMatcher) { | ||
41 | MatcherReference key = new MatcherReference(lsMatcher.getPlanDescriptor().getQuery(), | ||
42 | lsMatcher.getPlanDescriptor().getAdornment()); | ||
43 | planReference.put(key, lsMatcher.getPlan().stream().map(SearchPlanExecutor::getSearchPlan).collect(Collectors.toList())); | ||
44 | } | ||
45 | |||
46 | @Override | ||
47 | public void operationExecuted(SearchPlan plan, ISearchOperation operation, MatchingFrame frame, boolean isSuccessful) { | ||
48 | Map<ISearchOperation, Integer> counts = isSuccessful ? successfulOperationCounts : failedOperationCounts; | ||
49 | counts.merge(operation, | ||
50 | /*no previous entry*/1, | ||
51 | /*increase previous value*/(oldValue, v) -> oldValue + 1); | ||
52 | } | ||
53 | |||
54 | @Override | ||
55 | public String toString() { | ||
56 | StringBuilder sb = new StringBuilder(); | ||
57 | for (java.util.Map.Entry<MatcherReference, List<SearchPlan>> entry: planReference.entrySet()){ | ||
58 | sb.append(entry.getKey()); | ||
59 | sb.append("\n"); | ||
60 | |||
61 | sb.append(entry.getValue()); | ||
62 | |||
63 | List<SearchPlan> bodies = entry.getValue(); | ||
64 | sb.append("{\n"); | ||
65 | for(int i=0;i<bodies.size();i++){ | ||
66 | sb.append("\tbody #");sb.append(i);sb.append("(\n"); | ||
67 | for(ISearchOperation operation : bodies.get(i).getOperations()){ | ||
68 | final int successCount = successfulOperationCounts.computeIfAbsent(operation, op -> 0); | ||
69 | final int failCount = failedOperationCounts.computeIfAbsent(operation, op -> 0); | ||
70 | sb.append("\t\t");sb.append(successCount); | ||
71 | sb.append("\t");sb.append(failCount); | ||
72 | sb.append("\t");sb.append(successCount + failCount); | ||
73 | sb.append("\t");sb.append(operation); | ||
74 | sb.append("\n"); | ||
75 | } | ||
76 | sb.append("\t)\n"); | ||
77 | } | ||
78 | sb.append("}\n"); | ||
79 | } | ||
80 | return sb.toString(); | ||
81 | } | ||
82 | |||
83 | } | ||