diff options
Diffstat (limited to 'subprojects/interpreter-localsearch')
77 files changed, 8502 insertions, 0 deletions
diff --git a/subprojects/interpreter-localsearch/NOTICE.md b/subprojects/interpreter-localsearch/NOTICE.md new file mode 100644 index 00000000..94c1d803 --- /dev/null +++ b/subprojects/interpreter-localsearch/NOTICE.md | |||
@@ -0,0 +1,94 @@ | |||
1 | <!-- | ||
2 | Copyright (c) 2018-2019, Zoltan Ujhelyi, IncQuery Labs Ltd. | ||
3 | Copyright (c) 2023 The Refinery Authors <https://refinery.tools/> | ||
4 | |||
5 | SPDX-License-Identifier: EPL-2.0 | ||
6 | --> | ||
7 | |||
8 | This module contains source code from the [Eclipse VIATRA™ project](https://projects.eclipse.org/projects/modeling.viatra), which is available under the terms of the [Eclipse Public License v 2.0](http://www.eclipse.org/legal/epl-v20.html). | ||
9 | |||
10 | Eclipse®, VIATRA™ and ‘Eclipse VIATRA™’ are trademarks of Eclipse Foundation, Inc. | ||
11 | |||
12 | To comply with the [Eclipse Foundation Trademark Usage Policy](https://www.eclipse.org/legal/logo_guidelines.php), we replaced the Eclipse VIATRA™ branding in this repository with the name _Refinery Interpreter_, since the [Eclipse Public License - v 2.0](https://www.eclipse.org/legal/epl-2.0/) does not grant trademark usage rights to derived projects. | ||
13 | _Refinery Interpreter_ is not endorsed or sponsored by Eclipse Foundation, Inc. or any other organizations using the Eclipse VIATRA™ trademark. | ||
14 | |||
15 | We publish our modifications to Eclipse VIATRA™ for use within the Refinery framework under the name _Refinery Interpreter_. When not using _Refinery Interpreter_ directly in the Refinery framework, users are strongly encouraged to rely on [Eclipse VIATRA™](https://eclipse.dev/viatra/) instead. In particular, _Refinery Interpreter_ does not provide any API stability guarantees that are provided by Eclipse VIATRA™. | ||
16 | |||
17 | We reproduce the [accompanying notices of Eclipse VIATRA™](https://github.com/viatra/org.eclipse.viatra/blob/d422bcc626a99c4640c0d13931f393ccea0d2326/NOTICE.md) in full below: | ||
18 | |||
19 | # Notices for Eclipse VIATRA™ | ||
20 | |||
21 | This content is produced and maintained by the Eclipse VIATRA project. | ||
22 | |||
23 | * Project home: https://projects.eclipse.org/projects/modeling.viatra | ||
24 | |||
25 | ## Trademarks | ||
26 | |||
27 | Eclipse VIATRA, and VIATRA are trademarks of the Eclipse Foundation. | ||
28 | |||
29 | ## Copyright | ||
30 | |||
31 | All content is the property of the respective authors or their employers. For | ||
32 | more information regarding authorship of content, please consult the listed | ||
33 | source code repository logs. | ||
34 | |||
35 | ## Declared Project Licenses | ||
36 | |||
37 | This program and the accompanying materials are made available under the terms | ||
38 | of the Eclipse Public License v. 2.0 which is available at | ||
39 | http://www.eclipse.org/legal/epl-v20.html. | ||
40 | |||
41 | SPDX-License-Identifier: EPL-2.0 | ||
42 | |||
43 | ## Source Code | ||
44 | |||
45 | The project maintains the following source code repositories: | ||
46 | |||
47 | * http://git.eclipse.org/c/viatra/org.eclipse.viatra.git | ||
48 | * http://git.eclipse.org/c/viatra/org.eclipse.viatra.modelobfuscator.git | ||
49 | * http://git.eclipse.org/c/viatra/org.eclipse.viatra.examples.git | ||
50 | * http://git.eclipse.org/c/viatra/org.eclipse.viatra2.vpm.git | ||
51 | |||
52 | ## Third-party Content | ||
53 | |||
54 | This project leverages the following third party content. | ||
55 | |||
56 | ANTLR Runtime only: (3.2) | ||
57 | |||
58 | * License: New BSD license | ||
59 | |||
60 | Apache Commons Language Library (2.1) | ||
61 | |||
62 | * License: Apache License, 2.0 | ||
63 | |||
64 | Google Guice / Inject Core API (3.0.0) | ||
65 | |||
66 | * License: Apache License, 2.0 | ||
67 | |||
68 | Google Guice / Inject Core API (3.0.0) | ||
69 | |||
70 | * License: Apache License 2.0 | ||
71 | |||
72 | Guava (10.0.1) | ||
73 | |||
74 | * License: Apache License, 2.0 | ||
75 | |||
76 | Guice (2.0) | ||
77 | |||
78 | * License: Apache License, 2.0 | ||
79 | |||
80 | guice-multibindings (3.0.0) | ||
81 | |||
82 | * License: Apache License, 2.0 | ||
83 | |||
84 | log4j (1.2.15) | ||
85 | |||
86 | * License: Apache License 2.0 | ||
87 | |||
88 | LPG Java Runtime (lpgjavaruntime.jar) (1.1) | ||
89 | |||
90 | * License: Eclipse Public License | ||
91 | |||
92 | mockito (1.9.5) | ||
93 | |||
94 | * License: Apache License, 2.0, New BSD license, MIT license | ||
diff --git a/subprojects/interpreter-localsearch/about.html b/subprojects/interpreter-localsearch/about.html new file mode 100644 index 00000000..d1d5593a --- /dev/null +++ b/subprojects/interpreter-localsearch/about.html | |||
@@ -0,0 +1,26 @@ | |||
1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> | ||
2 | <html> | ||
3 | <!-- | ||
4 | Copyright (c) 2017, Eclipse.org Foundation, Inc. | ||
5 | |||
6 | SPDX-License-Identifier: LicenseRef-EPL-Steward | ||
7 | --> | ||
8 | <head> | ||
9 | <title>About</title> | ||
10 | <meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1"> | ||
11 | </head> | ||
12 | <body lang="EN-US"> | ||
13 | <h2>About This Content</h2> | ||
14 | |||
15 | <p>March 18, 2019</p> | ||
16 | <h3>License</h3> | ||
17 | |||
18 | <p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the | ||
19 | Eclipse Public License Version 2.0 ("EPL"). A copy of the EPL is available at <a href="http://www.eclipse.org/org/documents/epl-v20.php">http://www.eclipse.org/legal/epl-v20.html</a>. | ||
20 | For purposes of the EPL, "Program" will mean the Content.</p> | ||
21 | |||
22 | <p>If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may | ||
23 | apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise | ||
24 | indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p> | ||
25 | </body> | ||
26 | </html> | ||
diff --git a/subprojects/interpreter-localsearch/build.gradle.kts b/subprojects/interpreter-localsearch/build.gradle.kts new file mode 100644 index 00000000..dae92c6c --- /dev/null +++ b/subprojects/interpreter-localsearch/build.gradle.kts | |||
@@ -0,0 +1,14 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | |||
7 | plugins { | ||
8 | id("tools.refinery.gradle.interpreter-library") | ||
9 | } | ||
10 | |||
11 | dependencies { | ||
12 | implementation(project(":refinery-interpreter")) | ||
13 | implementation(libs.slf4j.log4j) | ||
14 | } | ||
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 | } | ||