aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java/tools
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-query-viatra/src/main/java/tools')
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java21
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryAdapter.java10
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java18
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java86
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java119
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java44
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java97
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java53
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java54
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java76
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java117
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java35
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java137
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java60
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java28
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java70
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java32
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java52
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java88
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java53
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java115
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java20
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java47
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java80
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java55
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java21
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java223
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java266
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java189
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java72
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java65
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java55
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java)16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java37
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java19
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java44
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java48
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java65
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java37
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java44
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java)18
48 files changed, 2177 insertions, 662 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java
deleted file mode 100644
index 677e3c7d..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQuery.java
+++ /dev/null
@@ -1,21 +0,0 @@
1package tools.refinery.store.query.viatra;
2
3import tools.refinery.store.adapter.ModelAdapterBuilderFactory;
4import tools.refinery.store.model.ModelStoreBuilder;
5import tools.refinery.store.query.ModelQuery;
6import tools.refinery.store.query.viatra.internal.ViatraModelQueryBuilderImpl;
7
8public final class ViatraModelQuery extends ModelAdapterBuilderFactory<ViatraModelQueryAdapter,
9 ViatraModelQueryStoreAdapter, ViatraModelQueryBuilder> {
10 public static final ViatraModelQuery ADAPTER = new ViatraModelQuery();
11
12 private ViatraModelQuery() {
13 super(ViatraModelQueryAdapter.class, ViatraModelQueryStoreAdapter.class, ViatraModelQueryBuilder.class);
14 extendsAdapter(ModelQuery.ADAPTER);
15 }
16
17 @Override
18 public ViatraModelQueryBuilder createBuilder(ModelStoreBuilder storeBuilder) {
19 return new ViatraModelQueryBuilderImpl(storeBuilder);
20 }
21}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryAdapter.java
index 7e21476b..12c93f62 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryAdapter.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryAdapter.java
@@ -1,8 +1,18 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
2 7
3import tools.refinery.store.query.ModelQueryAdapter; 8import tools.refinery.store.query.ModelQueryAdapter;
9import tools.refinery.store.query.viatra.internal.ViatraModelQueryBuilderImpl;
4 10
5public interface ViatraModelQueryAdapter extends ModelQueryAdapter { 11public interface ViatraModelQueryAdapter extends ModelQueryAdapter {
6 @Override 12 @Override
7 ViatraModelQueryStoreAdapter getStoreAdapter(); 13 ViatraModelQueryStoreAdapter getStoreAdapter();
14
15 static ViatraModelQueryBuilder builder() {
16 return new ViatraModelQueryBuilderImpl();
17 }
8} 18}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
index efc6146c..931a07aa 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
@@ -1,10 +1,16 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
2 7
3import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 8import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
4import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 9import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
5import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 10import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
6import tools.refinery.store.model.ModelStore; 11import tools.refinery.store.model.ModelStore;
7import tools.refinery.store.query.DNF; 12import tools.refinery.store.query.dnf.AnyQuery;
13import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.ModelQueryBuilder; 14import tools.refinery.store.query.ModelQueryBuilder;
9 15
10import java.util.Collection; 16import java.util.Collection;
@@ -23,26 +29,26 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder {
23 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); 29 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory);
24 30
25 @Override 31 @Override
26 default ViatraModelQueryBuilder queries(DNF... queries) { 32 default ViatraModelQueryBuilder queries(AnyQuery... queries) {
27 ModelQueryBuilder.super.queries(queries); 33 ModelQueryBuilder.super.queries(queries);
28 return this; 34 return this;
29 } 35 }
30 36
31 @Override 37 @Override
32 default ViatraModelQueryBuilder queries(Collection<DNF> queries) { 38 default ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) {
33 ModelQueryBuilder.super.queries(queries); 39 ModelQueryBuilder.super.queries(queries);
34 return this; 40 return this;
35 } 41 }
36 42
37 @Override 43 @Override
38 ViatraModelQueryBuilder query(DNF query); 44 ViatraModelQueryBuilder query(AnyQuery query);
39 45
40 ViatraModelQueryBuilder query(DNF query, QueryEvaluationHint queryEvaluationHint); 46 ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint);
41 47
42 ViatraModelQueryBuilder computeHint(Function<DNF, QueryEvaluationHint> computeHint); 48 ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint);
43 49
44 ViatraModelQueryBuilder hint(DNF dnf, QueryEvaluationHint queryEvaluationHint); 50 ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint);
45 51
46 @Override 52 @Override
47 ViatraModelQueryStoreAdapter createStoreAdapter(ModelStore store); 53 ViatraModelQueryStoreAdapter build(ModelStore store);
48} 54}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
index 1ee02f12..da6d7bd5 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryStoreAdapter.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra; 6package tools.refinery.store.query.viatra;
2 7
3import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 8import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java
deleted file mode 100644
index 46c28434..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java
+++ /dev/null
@@ -1,18 +0,0 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.tuple.Tuple1;
5import tools.refinery.store.tuple.TupleLike;
6
7public record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike {
8 @Override
9 public int getSize() {
10 return wrappedTuple.getSize();
11 }
12
13 @Override
14 public int get(int element) {
15 var wrappedValue = (Tuple1) wrappedTuple.get(element);
16 return wrappedValue.value0();
17 }
18}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
index 8328e759..d1a65a89 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/RelationalScope.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
2 7
3import org.apache.log4j.Logger; 8import org.apache.log4j.Logger;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
index 039f46fa..5f3e86b4 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
2 7
3import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine; 8import org.eclipse.viatra.query.runtime.api.AdvancedViatraQueryEngine;
@@ -7,62 +12,92 @@ import org.eclipse.viatra.query.runtime.internal.apiimpl.ViatraQueryEngineImpl;
7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend; 12import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 13import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
9import tools.refinery.store.model.Model; 14import tools.refinery.store.model.Model;
10import tools.refinery.store.query.DNF; 15import tools.refinery.store.model.ModelListener;
11import tools.refinery.store.query.ResultSet; 16import tools.refinery.store.query.resultset.AnyResultSet;
17import tools.refinery.store.query.resultset.EmptyResultSet;
18import tools.refinery.store.query.resultset.ResultSet;
19import tools.refinery.store.query.dnf.AnyQuery;
20import tools.refinery.store.query.dnf.FunctionalQuery;
21import tools.refinery.store.query.dnf.Query;
22import tools.refinery.store.query.dnf.RelationalQuery;
12import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; 23import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
24import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher;
25import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
26import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher;
13 27
14import java.lang.invoke.MethodHandle; 28import java.lang.invoke.MethodHandle;
15import java.lang.invoke.MethodHandles; 29import java.lang.invoke.MethodHandles;
16import java.util.Collection; 30import java.util.Collection;
17import java.util.Collections; 31import java.util.Collections;
18import java.util.HashMap; 32import java.util.LinkedHashMap;
19import java.util.Map; 33import java.util.Map;
20 34
21public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { 35public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, ModelListener {
22 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery"; 36 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery";
37 private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE;
23 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends"; 38 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends";
39 private static final MethodHandle GET_QUERY_BACKENDS_HANDLE;
24 40
25 private final Model model; 41 private final Model model;
26 private final ViatraModelQueryStoreAdapterImpl storeAdapter; 42 private final ViatraModelQueryStoreAdapterImpl storeAdapter;
27 private final ViatraQueryEngineImpl queryEngine; 43 private final ViatraQueryEngineImpl queryEngine;
28 private final MethodHandle setUpdatePropagationDelayedHandle; 44 private final Map<AnyQuery, AnyResultSet> resultSets;
29 private final MethodHandle getQueryBackendsHandle;
30 private final Map<DNF, ResultSet> resultSets;
31 private boolean pendingChanges; 45 private boolean pendingChanges;
32 46
33 ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { 47 static {
34 this.model = model;
35 this.storeAdapter = storeAdapter;
36 var scope = new RelationalScope(this);
37 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope);
38
39 try { 48 try {
40 var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup()); 49 var lookup = MethodHandles.privateLookupIn(ViatraQueryEngineImpl.class, MethodHandles.lookup());
41 setUpdatePropagationDelayedHandle = lookup.findSetter(ViatraQueryEngineImpl.class, 50 SET_UPDATE_PROPAGATION_DELAYED_HANDLE = lookup.findSetter(ViatraQueryEngineImpl.class,
42 DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE); 51 DELAY_MESSAGE_DELIVERY_FIELD_NAME, Boolean.TYPE);
43 getQueryBackendsHandle = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME, 52 GET_QUERY_BACKENDS_HANDLE = lookup.findGetter(ViatraQueryEngineImpl.class, QUERY_BACKENDS_FIELD_NAME,
44 Map.class); 53 Map.class);
45 } catch (IllegalAccessException | NoSuchFieldException e) { 54 } catch (IllegalAccessException | NoSuchFieldException e) {
46 throw new IllegalStateException("Cannot access private members of %s" 55 throw new IllegalStateException("Cannot access private members of %s"
47 .formatted(ViatraQueryEngineImpl.class.getName()), e); 56 .formatted(ViatraQueryEngineImpl.class.getName()), e);
48 } 57 }
58 }
59
60 ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) {
61 this.model = model;
62 this.storeAdapter = storeAdapter;
63 var scope = new RelationalScope(this);
64 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope,
65 storeAdapter.getEngineOptions());
49 66
50 var querySpecifications = storeAdapter.getQuerySpecifications(); 67 var querySpecifications = storeAdapter.getQuerySpecifications();
51 GenericQueryGroup.of( 68 GenericQueryGroup.of(
52 Collections.<IQuerySpecification<?>>unmodifiableCollection(querySpecifications.values()).stream() 69 Collections.<IQuerySpecification<?>>unmodifiableCollection(querySpecifications.values()).stream()
53 ).prepare(queryEngine); 70 ).prepare(queryEngine);
54 resultSets = new HashMap<>(querySpecifications.size()); 71 var vacuousQueries = storeAdapter.getVacuousQueries();
72 resultSets = new LinkedHashMap<>(querySpecifications.size() + vacuousQueries.size());
55 for (var entry : querySpecifications.entrySet()) { 73 for (var entry : querySpecifications.entrySet()) {
56 var matcher = queryEngine.getMatcher(entry.getValue()); 74 var rawPatternMatcher = queryEngine.getMatcher(entry.getValue());
57 resultSets.put(entry.getKey(), matcher); 75 var query = entry.getKey();
76 resultSets.put(query, createResultSet((Query<?>) query, rawPatternMatcher));
77 }
78 for (var vacuousQuery : vacuousQueries) {
79 resultSets.put(vacuousQuery, new EmptyResultSet<>(this, (Query<?>) vacuousQuery));
58 } 80 }
59 81
60 setUpdatePropagationDelayed(true); 82 setUpdatePropagationDelayed(true);
83 model.addListener(this);
84 }
85
86 private <T> ResultSet<T> createResultSet(Query<T> query, RawPatternMatcher matcher) {
87 if (query instanceof RelationalQuery relationalQuery) {
88 @SuppressWarnings("unchecked")
89 var resultSet = (ResultSet<T>) new RelationalViatraMatcher(this, relationalQuery, matcher);
90 return resultSet;
91 } else if (query instanceof FunctionalQuery<T> functionalQuery) {
92 return new FunctionalViatraMatcher<>(this, functionalQuery, matcher);
93 } else {
94 throw new IllegalArgumentException("Unknown query: " + query);
95 }
61 } 96 }
62 97
63 private void setUpdatePropagationDelayed(boolean value) { 98 private void setUpdatePropagationDelayed(boolean value) {
64 try { 99 try {
65 setUpdatePropagationDelayedHandle.invokeExact(queryEngine, value); 100 SET_UPDATE_PROPAGATION_DELAYED_HANDLE.invokeExact(queryEngine, value);
66 } catch (Error e) { 101 } catch (Error e) {
67 // Fatal JVM errors should not be wrapped. 102 // Fatal JVM errors should not be wrapped.
68 throw e; 103 throw e;
@@ -74,7 +109,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
74 private Collection<IQueryBackend> getQueryBackends() { 109 private Collection<IQueryBackend> getQueryBackends() {
75 try { 110 try {
76 @SuppressWarnings("unchecked") 111 @SuppressWarnings("unchecked")
77 var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) getQueryBackendsHandle.invokeExact(queryEngine); 112 var backendMap = (Map<IQueryBackendFactory, IQueryBackend>) GET_QUERY_BACKENDS_HANDLE.invokeExact(queryEngine);
78 return backendMap.values(); 113 return backendMap.values();
79 } catch (Error e) { 114 } catch (Error e) {
80 // Fatal JVM errors should not be wrapped. 115 // Fatal JVM errors should not be wrapped.
@@ -95,12 +130,14 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
95 } 130 }
96 131
97 @Override 132 @Override
98 public ResultSet getResultSet(DNF query) { 133 public <T> ResultSet<T> getResultSet(Query<T> query) {
99 var resultSet = resultSets.get(query); 134 var resultSet = resultSets.get(query);
100 if (resultSet == null) { 135 if (resultSet == null) {
101 throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name())); 136 throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name()));
102 } 137 }
103 return resultSet; 138 @SuppressWarnings("unchecked")
139 var typedResultSet = (ResultSet<T>) resultSet;
140 return typedResultSet;
104 } 141 }
105 142
106 @Override 143 @Override
@@ -132,4 +169,9 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
132 } 169 }
133 pendingChanges = false; 170 pendingChanges = false;
134 } 171 }
172
173 @Override
174 public void afterRestore() {
175 flushChanges();
176 }
135} 177}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
index 9f1e55b1..ce2467b4 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
@@ -1,115 +1,162 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
2 7
3import org.eclipse.viatra.query.runtime.api.IQuerySpecification; 8import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 9import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; 10import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHintOptions;
6import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 11import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
7import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 12import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
8import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory; 13import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory;
9import tools.refinery.store.adapter.AbstractModelAdapterBuilder; 14import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
10import tools.refinery.store.model.ModelStore; 15import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.model.ModelStoreBuilder; 16import tools.refinery.store.model.ModelStoreBuilder;
12import tools.refinery.store.query.DNF; 17import tools.refinery.store.query.dnf.AnyQuery;
18import tools.refinery.store.query.dnf.Dnf;
13import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; 19import tools.refinery.store.query.viatra.ViatraModelQueryBuilder;
14import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery; 20import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction;
15import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher; 21import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory;
22import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
23import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery;
16 24
17import java.util.Collections; 25import java.util.*;
18import java.util.LinkedHashMap;
19import java.util.Map;
20import java.util.function.Function; 26import java.util.function.Function;
21 27
22public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder { 28public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<ViatraModelQueryStoreAdapterImpl>
29 implements ViatraModelQueryBuilder {
23 private ViatraQueryEngineOptions.Builder engineOptionsBuilder; 30 private ViatraQueryEngineOptions.Builder engineOptionsBuilder;
24 private final DNF2PQuery dnf2PQuery = new DNF2PQuery(); 31 private QueryEvaluationHint defaultHint = new QueryEvaluationHint(Map.of(
25 private final Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>(); 32 // Use a cost function that ignores the initial (empty) model but allows higher arity input keys.
33 LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction()
34 ), (IQueryBackendFactory) null);
35 private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery();
36 private final Set<AnyQuery> vacuousQueries = new LinkedHashSet<>();
37 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>();
26 38
27 public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) { 39 public ViatraModelQueryBuilderImpl() {
28 super(storeBuilder);
29 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder() 40 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder()
30 .withDefaultBackend(ReteBackendFactory.INSTANCE) 41 .withDefaultBackend(ReteBackendFactory.INSTANCE)
31 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE) 42 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE)
32 .withDefaultSearchBackend(LocalSearchGenericBackendFactory.INSTANCE); 43 .withDefaultSearchBackend(RelationalLocalSearchBackendFactory.INSTANCE);
33 } 44 }
34 45
35 @Override 46 @Override
36 public ViatraModelQueryBuilder engineOptions(ViatraQueryEngineOptions engineOptions) { 47 public ViatraModelQueryBuilder engineOptions(ViatraQueryEngineOptions engineOptions) {
48 checkNotConfigured();
37 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder(engineOptions); 49 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder(engineOptions);
38 return this; 50 return this;
39 } 51 }
40 52
41 @Override 53 @Override
42 public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) { 54 public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) {
43 engineOptionsBuilder.withDefaultHint(queryEvaluationHint); 55 checkNotConfigured();
56 defaultHint = defaultHint.overrideBy(queryEvaluationHint);
44 return this; 57 return this;
45 } 58 }
46 59
47 @Override 60 @Override
48 public ViatraModelQueryBuilder backend(IQueryBackendFactory queryBackendFactory) { 61 public ViatraModelQueryBuilder backend(IQueryBackendFactory queryBackendFactory) {
62 checkNotConfigured();
49 engineOptionsBuilder.withDefaultBackend(queryBackendFactory); 63 engineOptionsBuilder.withDefaultBackend(queryBackendFactory);
50 return this; 64 return this;
51 } 65 }
52 66
53 @Override 67 @Override
54 public ViatraModelQueryBuilder cachingBackend(IQueryBackendFactory queryBackendFactory) { 68 public ViatraModelQueryBuilder cachingBackend(IQueryBackendFactory queryBackendFactory) {
69 checkNotConfigured();
55 engineOptionsBuilder.withDefaultCachingBackend(queryBackendFactory); 70 engineOptionsBuilder.withDefaultCachingBackend(queryBackendFactory);
56 return this; 71 return this;
57 } 72 }
58 73
59 @Override 74 @Override
60 public ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory) { 75 public ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory) {
76 checkNotConfigured();
61 engineOptionsBuilder.withDefaultSearchBackend(queryBackendFactory); 77 engineOptionsBuilder.withDefaultSearchBackend(queryBackendFactory);
62 return this; 78 return this;
63 } 79 }
64 80
65 @Override 81 @Override
66 public ViatraModelQueryBuilder query(DNF query) { 82 public ViatraModelQueryBuilder query(AnyQuery query) {
67 if (querySpecifications.containsKey(query)) { 83 checkNotConfigured();
68 throw new IllegalArgumentException("%s was already added to the query engine".formatted(query.name())); 84 if (querySpecifications.containsKey(query) || vacuousQueries.contains(query)) {
85 // Ignore duplicate queries.
86 return this;
87 }
88 var dnf = query.getDnf();
89 var reduction = dnf.getReduction();
90 switch (reduction) {
91 case NOT_REDUCIBLE -> {
92 var pQuery = dnf2PQuery.translate(dnf);
93 querySpecifications.put(query, pQuery.build());
94 }
95 case ALWAYS_FALSE -> vacuousQueries.add(query);
96 case ALWAYS_TRUE -> throw new IllegalArgumentException(
97 "Query %s is relationally unsafe (it matches every tuple)".formatted(query.name()));
98 default -> throw new IllegalArgumentException("Unknown reduction: " + reduction);
69 } 99 }
70 var pQuery = dnf2PQuery.translate(query);
71 querySpecifications.put(query, pQuery.build());
72 return this; 100 return this;
73 } 101 }
74 102
75 @Override 103 @Override
76 public ViatraModelQueryBuilder query(DNF query, QueryEvaluationHint queryEvaluationHint) { 104 public ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint) {
105 hint(query.getDnf(), queryEvaluationHint);
77 query(query); 106 query(query);
78 hint(query, queryEvaluationHint);
79 return this; 107 return this;
80 } 108 }
81 109
82 @Override 110 @Override
83 public ViatraModelQueryBuilder computeHint(Function<DNF, QueryEvaluationHint> computeHint) { 111 public ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint) {
112 checkNotConfigured();
84 dnf2PQuery.setComputeHint(computeHint); 113 dnf2PQuery.setComputeHint(computeHint);
85 return this; 114 return this;
86 } 115 }
87 116
88 @Override 117 @Override
89 public ViatraModelQueryBuilder hint(DNF dnf, QueryEvaluationHint queryEvaluationHint) { 118 public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) {
90 var pQuery = dnf2PQuery.getAlreadyTranslated(dnf); 119 checkNotConfigured();
91 if (pQuery == null) { 120 dnf2PQuery.hint(dnf, queryEvaluationHint);
92 throw new IllegalArgumentException(
93 "Cannot specify hint for %s, because it was not added to the query engine".formatted(dnf.name()));
94 }
95 pQuery.setEvaluationHints(pQuery.getEvaluationHints().overrideBy(queryEvaluationHint));
96 return this; 121 return this;
97 } 122 }
98 123
99 @Override 124 @Override
100 public ViatraModelQueryStoreAdapterImpl createStoreAdapter(ModelStore store) { 125 public void doConfigure(ModelStoreBuilder storeBuilder) {
126 dnf2PQuery.assertNoUnusedHints();
127 }
128
129 @Override
130 public ViatraModelQueryStoreAdapterImpl doBuild(ModelStore store) {
101 validateSymbols(store); 131 validateSymbols(store);
102 return new ViatraModelQueryStoreAdapterImpl(store, engineOptionsBuilder.build(), dnf2PQuery.getRelationViews(), 132 return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getSymbolViews(),
103 Collections.unmodifiableMap(querySpecifications)); 133 Collections.unmodifiableMap(querySpecifications), Collections.unmodifiableSet(vacuousQueries));
134 }
135
136 private ViatraQueryEngineOptions buildEngineOptions() {
137 // Workaround: manually override the default backend, because {@link ViatraQueryEngineOptions.Builder}
138 // ignores all backend requirements except {@code SPECIFIC}.
139 switch (defaultHint.getQueryBackendRequirementType()) {
140 case SPECIFIC -> engineOptionsBuilder.withDefaultBackend(defaultHint.getQueryBackendFactory());
141 case DEFAULT_CACHING -> engineOptionsBuilder.withDefaultBackend(
142 engineOptionsBuilder.build().getDefaultCachingBackendFactory());
143 case DEFAULT_SEARCH -> engineOptionsBuilder.withDefaultBackend(
144 engineOptionsBuilder.build().getDefaultSearchBackendFactory());
145 case UNSPECIFIED -> {
146 // Nothing to do, leave the default backend unchanged.
147 }
148 }
149 engineOptionsBuilder.withDefaultHint(defaultHint);
150 return engineOptionsBuilder.build();
104 } 151 }
105 152
106 private void validateSymbols(ModelStore store) { 153 private void validateSymbols(ModelStore store) {
107 var symbols = store.getSymbols(); 154 var symbols = store.getSymbols();
108 for (var relationView : dnf2PQuery.getRelationViews().keySet()) { 155 for (var symbolView : dnf2PQuery.getSymbolViews().keySet()) {
109 var symbol = relationView.getSymbol(); 156 var symbol = symbolView.getSymbol();
110 if (!symbols.contains(symbol)) { 157 if (!symbols.contains(symbol)) {
111 throw new IllegalArgumentException("Cannot query relation view %s: symbol %s is not in the model" 158 throw new IllegalArgumentException("Cannot query view %s: symbol %s is not in the model"
112 .formatted(relationView, symbol)); 159 .formatted(symbolView, symbol));
113 } 160 }
114 } 161 }
115 } 162 }
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
index 394e407e..11a3c7fd 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal; 6package tools.refinery.store.query.viatra.internal;
2 7
3import org.eclipse.viatra.query.runtime.api.IQuerySpecification; 8import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
@@ -5,27 +10,34 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 10import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
6import tools.refinery.store.model.Model; 11import tools.refinery.store.model.Model;
7import tools.refinery.store.model.ModelStore; 12import tools.refinery.store.model.ModelStore;
8import tools.refinery.store.query.DNF; 13import tools.refinery.store.query.dnf.AnyQuery;
9import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter; 14import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter;
10import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher; 15import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
11import tools.refinery.store.query.view.AnyRelationView; 16import tools.refinery.store.query.view.AnySymbolView;
12 17
13import java.util.Collection; 18import java.util.*;
14import java.util.Map;
15 19
16public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAdapter { 20public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAdapter {
17 private final ModelStore store; 21 private final ModelStore store;
18 private final ViatraQueryEngineOptions engineOptions; 22 private final ViatraQueryEngineOptions engineOptions;
19 private final Map<AnyRelationView, IInputKey> inputKeys; 23 private final Map<AnySymbolView, IInputKey> inputKeys;
20 private final Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications; 24 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications;
25 private final Set<AnyQuery> vacuousQueries;
26 private final Set<AnyQuery> allQueries;
21 27
22 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, 28 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions,
23 Map<AnyRelationView, IInputKey> inputKeys, 29 Map<AnySymbolView, IInputKey> inputKeys,
24 Map<DNF, IQuerySpecification<RawPatternMatcher>> querySpecifications) { 30 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications,
31 Set<AnyQuery> vacuousQueries) {
25 this.store = store; 32 this.store = store;
26 this.engineOptions = engineOptions; 33 this.engineOptions = engineOptions;
27 this.inputKeys = inputKeys; 34 this.inputKeys = inputKeys;
28 this.querySpecifications = querySpecifications; 35 this.querySpecifications = querySpecifications;
36 this.vacuousQueries = vacuousQueries;
37 var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size());
38 mutableAllQueries.addAll(querySpecifications.keySet());
39 mutableAllQueries.addAll(vacuousQueries);
40 this.allQueries = Collections.unmodifiableSet(mutableAllQueries);
29 } 41 }
30 42
31 @Override 43 @Override
@@ -33,23 +45,27 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd
33 return store; 45 return store;
34 } 46 }
35 47
36 public Collection<AnyRelationView> getRelationViews() { 48 public Collection<AnySymbolView> getSymbolViews() {
37 return inputKeys.keySet(); 49 return inputKeys.keySet();
38 } 50 }
39 51
40 public Map<AnyRelationView, IInputKey> getInputKeys() { 52 public Map<AnySymbolView, IInputKey> getInputKeys() {
41 return inputKeys; 53 return inputKeys;
42 } 54 }
43 55
44 @Override 56 @Override
45 public Collection<DNF> getQueries() { 57 public Collection<AnyQuery> getQueries() {
46 return querySpecifications.keySet(); 58 return allQueries;
47 } 59 }
48 60
49 Map<DNF, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() { 61 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() {
50 return querySpecifications; 62 return querySpecifications;
51 } 63 }
52 64
65 Set<AnyQuery> getVacuousQueries() {
66 return vacuousQueries;
67 }
68
53 @Override 69 @Override
54 public ViatraQueryEngineOptions getEngineOptions() { 70 public ViatraQueryEngineOptions getEngineOptions() {
55 return engineOptions; 71 return engineOptions;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java
deleted file mode 100644
index e0bca9e0..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/cardinality/UpperCardinalitySumAggregationOperator.java
+++ /dev/null
@@ -1,97 +0,0 @@
1package tools.refinery.store.query.viatra.internal.cardinality;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator;
4import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
5import tools.refinery.store.representation.cardinality.FiniteUpperCardinality;
6import tools.refinery.store.representation.cardinality.UnboundedUpperCardinality;
7import tools.refinery.store.representation.cardinality.UpperCardinalities;
8import tools.refinery.store.representation.cardinality.UpperCardinality;
9
10import java.util.stream.Stream;
11
12public class UpperCardinalitySumAggregationOperator implements IMultisetAggregationOperator<UpperCardinality,
13 UpperCardinalitySumAggregationOperator.Accumulator, UpperCardinality> {
14 public static final UpperCardinalitySumAggregationOperator INSTANCE = new UpperCardinalitySumAggregationOperator();
15
16 public static final BoundAggregator BOUND_AGGREGATOR = new BoundAggregator(INSTANCE, UpperCardinality.class,
17 UpperCardinality.class);
18
19 private UpperCardinalitySumAggregationOperator() {
20 // Singleton constructor.
21 }
22
23 @Override
24 public String getName() {
25 return "sum<UpperCardinality>";
26 }
27
28 @Override
29 public String getShortDescription() {
30 return "%s computes the sum of finite or unbounded upper cardinalities".formatted(getName());
31 }
32
33 @Override
34 public Accumulator createNeutral() {
35 return new Accumulator();
36 }
37
38 @Override
39 public boolean isNeutral(Accumulator result) {
40 return result.sumFiniteUpperBounds == 0 && result.countUnbounded == 0;
41 }
42
43 @Override
44 public Accumulator update(Accumulator oldResult, UpperCardinality updateValue, boolean isInsertion) {
45 if (updateValue instanceof FiniteUpperCardinality finiteUpperCardinality) {
46 int finiteUpperBound = finiteUpperCardinality.finiteUpperBound();
47 if (isInsertion) {
48 oldResult.sumFiniteUpperBounds += finiteUpperBound;
49 } else {
50 oldResult.sumFiniteUpperBounds -= finiteUpperBound;
51 }
52 } else if (updateValue instanceof UnboundedUpperCardinality) {
53 if (isInsertion) {
54 oldResult.countUnbounded += 1;
55 } else {
56 oldResult.countUnbounded -= 1;
57 }
58 } else {
59 throw new IllegalArgumentException("Unknown UpperCardinality: " + updateValue);
60 }
61 return oldResult;
62 }
63
64 @Override
65 public UpperCardinality getAggregate(Accumulator result) {
66 return result.countUnbounded > 0 ? UpperCardinalities.UNBOUNDED :
67 UpperCardinalities.valueOf(result.sumFiniteUpperBounds);
68 }
69
70 @Override
71 public UpperCardinality aggregateStream(Stream<UpperCardinality> stream) {
72 var result = stream.collect(this::createNeutral, (accumulator, value) -> update(accumulator, value, true),
73 (left, right) -> new Accumulator(left.sumFiniteUpperBounds + right.sumFiniteUpperBounds,
74 left.countUnbounded + right.countUnbounded));
75 return getAggregate(result);
76 }
77
78 @Override
79 public Accumulator clone(Accumulator original) {
80 return new Accumulator(original.sumFiniteUpperBounds, original.countUnbounded);
81 }
82
83 public static class Accumulator {
84 private int sumFiniteUpperBounds;
85
86 private int countUnbounded;
87
88 private Accumulator(int sumFiniteUpperBounds, int countUnbounded) {
89 this.sumFiniteUpperBounds = sumFiniteUpperBounds;
90 this.countUnbounded = countUnbounded;
91 }
92
93 private Accumulator() {
94 this(0, 0);
95 }
96 }
97}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
index 2a24b67c..8cb199d2 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/DummyBaseIndexer.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
2 7
3import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; 8import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
index 28bc69d0..7220f8ca 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalEngineContext.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
2 7
3import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex; 8import org.eclipse.viatra.query.runtime.api.scope.IBaseIndex;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
index cba3fa08..211eacb4 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
@@ -1,10 +1,16 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
2 7
3import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext; 8import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext;
4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 9import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
5import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; 10import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication;
6import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper; 11import org.eclipse.viatra.query.runtime.matchers.context.common.JavaTransitiveInstancesKey;
7import tools.refinery.store.query.view.AnyRelationView; 12import tools.refinery.store.query.viatra.internal.pquery.SymbolViewWrapper;
13import tools.refinery.store.query.view.AnySymbolView;
8 14
9import java.util.*; 15import java.util.*;
10 16
@@ -12,9 +18,9 @@ import java.util.*;
12 * The meta context information for String scopes. 18 * The meta context information for String scopes.
13 */ 19 */
14public class RelationalQueryMetaContext extends AbstractQueryMetaContext { 20public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
15 private final Map<AnyRelationView, IInputKey> inputKeys; 21 private final Map<AnySymbolView, IInputKey> inputKeys;
16 22
17 RelationalQueryMetaContext(Map<AnyRelationView, IInputKey> inputKeys) { 23 RelationalQueryMetaContext(Map<AnySymbolView, IInputKey> inputKeys) {
18 this.inputKeys = inputKeys; 24 this.inputKeys = inputKeys;
19 } 25 }
20 26
@@ -37,26 +43,43 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
37 43
38 @Override 44 @Override
39 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) { 45 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) {
40 var relationView = checkKey(implyingKey); 46 if (implyingKey instanceof JavaTransitiveInstancesKey) {
41 var relationViewImplications = relationView.getImpliedRelationViews(); 47 return List.of();
48 }
49 var symbolView = checkKey(implyingKey);
50 var relationViewImplications = symbolView.getImpliedRelationViews();
42 var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size()); 51 var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size());
43 for (var relationViewImplication : relationViewImplications) { 52 for (var relationViewImplication : relationViewImplications) {
44 if (!relationView.equals(relationViewImplication.implyingRelationView())) { 53 if (!symbolView.equals(relationViewImplication.implyingView())) {
45 throw new IllegalArgumentException("Relation view %s returned unrelated implication %s".formatted( 54 throw new IllegalArgumentException("Relation view %s returned unrelated implication %s".formatted(
46 relationView, relationViewImplication)); 55 symbolView, relationViewImplication));
47 } 56 }
48 var impliedInputKey = inputKeys.get(relationViewImplication.impliedRelationView()); 57 var impliedInputKey = inputKeys.get(relationViewImplication.impliedView());
49 // Ignore implications not relevant for any queries included in the model. 58 // Ignore implications not relevant for any queries included in the model.
50 if (impliedInputKey != null) { 59 if (impliedInputKey != null) {
51 inputKeyImplications.add(new InputKeyImplication(implyingKey, impliedInputKey, 60 inputKeyImplications.add(new InputKeyImplication(implyingKey, impliedInputKey,
52 relationViewImplication.impliedIndices())); 61 relationViewImplication.impliedIndices()));
53 } 62 }
54 } 63 }
64 var parameters = symbolView.getParameters();
65 int arity = symbolView.arity();
66 for (int i = 0; i < arity; i++) {
67 var parameter = parameters.get(i);
68 var parameterType = parameter.tryGetType();
69 if (parameterType.isPresent()) {
70 var javaTransitiveInstancesKey = new JavaTransitiveInstancesKey(parameterType.get());
71 var javaImplication = new InputKeyImplication(implyingKey, javaTransitiveInstancesKey, List.of(i));
72 inputKeyImplications.add(javaImplication);
73 }
74 }
55 return inputKeyImplications; 75 return inputKeyImplications;
56 } 76 }
57 77
58 @Override 78 @Override
59 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) { 79 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) {
80 if (key instanceof JavaTransitiveInstancesKey) {
81 return Map.of();
82 }
60 var relationView = checkKey(key); 83 var relationView = checkKey(key);
61 var functionalDependencies = relationView.getFunctionalDependencies(); 84 var functionalDependencies = relationView.getFunctionalDependencies();
62 var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size()); 85 var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size());
@@ -75,20 +98,20 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
75 return flattened; 98 return flattened;
76 } 99 }
77 100
78 private static void checkValidIndices(AnyRelationView relationView, Collection<Integer> indices) { 101 private static void checkValidIndices(AnySymbolView relationView, Collection<Integer> indices) {
79 indices.stream().filter(relationView::invalidIndex).findAny().ifPresent(i -> { 102 indices.stream().filter(relationView::invalidIndex).findAny().ifPresent(i -> {
80 throw new IllegalArgumentException("Index %d is invalid for %s".formatted(i, relationView)); 103 throw new IllegalArgumentException("Index %d is invalid for %s".formatted(i, relationView));
81 }); 104 });
82 } 105 }
83 106
84 public AnyRelationView checkKey(IInputKey key) { 107 public AnySymbolView checkKey(IInputKey key) {
85 if (!(key instanceof RelationViewWrapper wrapper)) { 108 if (!(key instanceof SymbolViewWrapper wrapper)) {
86 throw new IllegalArgumentException("The input key %s is not a valid input key".formatted(key)); 109 throw new IllegalArgumentException("The input key %s is not a valid input key".formatted(key));
87 } 110 }
88 var relationView = wrapper.getWrappedKey(); 111 var symbolView = wrapper.getWrappedKey();
89 if (!inputKeys.containsKey(relationView)) { 112 if (!inputKeys.containsKey(symbolView)) {
90 throw new IllegalArgumentException("The input key %s is not present in the model".formatted(key)); 113 throw new IllegalArgumentException("The input key %s is not present in the model".formatted(key));
91 } 114 }
92 return relationView; 115 return symbolView;
93 } 116 }
94} 117}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
index 01d20d3e..0f2daca8 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.context; 6package tools.refinery.store.query.viatra.internal.context;
2 7
3import org.eclipse.viatra.query.runtime.matchers.context.*; 8import org.eclipse.viatra.query.runtime.matchers.context.*;
@@ -8,9 +13,9 @@ import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
8import org.eclipse.viatra.query.runtime.matchers.util.Accuracy; 13import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
9import tools.refinery.store.model.Model; 14import tools.refinery.store.model.Model;
10import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 15import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
11import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper; 16import tools.refinery.store.query.viatra.internal.pquery.SymbolViewWrapper;
12import tools.refinery.store.query.viatra.internal.update.ModelUpdateListener; 17import tools.refinery.store.query.viatra.internal.update.ModelUpdateListener;
13import tools.refinery.store.query.view.AnyRelationView; 18import tools.refinery.store.query.view.AnySymbolView;
14 19
15import java.lang.reflect.InvocationTargetException; 20import java.lang.reflect.InvocationTargetException;
16import java.util.Iterator; 21import java.util.Iterator;
@@ -54,8 +59,9 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
54 59
55 @Override 60 @Override
56 public boolean isIndexed(IInputKey key, IndexingService service) { 61 public boolean isIndexed(IInputKey key, IndexingService service) {
57 if (key instanceof AnyRelationView relationalKey) { 62 if (key instanceof SymbolViewWrapper wrapper) {
58 return this.modelUpdateListener.containsRelationView(relationalKey); 63 var symbolViewKey = wrapper.getWrappedKey();
64 return this.modelUpdateListener.containsSymbolView(symbolViewKey);
59 } else { 65 } else {
60 return false; 66 return false;
61 } 67 }
@@ -68,13 +74,13 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
68 } 74 }
69 } 75 }
70 76
71 AnyRelationView checkKey(IInputKey key) { 77 AnySymbolView checkKey(IInputKey key) {
72 if (key instanceof RelationViewWrapper wrappedKey) { 78 if (key instanceof SymbolViewWrapper wrappedKey) {
73 var relationViewKey = wrappedKey.getWrappedKey(); 79 var symbolViewKey = wrappedKey.getWrappedKey();
74 if (modelUpdateListener.containsRelationView(relationViewKey)) { 80 if (modelUpdateListener.containsSymbolView(symbolViewKey)) {
75 return relationViewKey; 81 return symbolViewKey;
76 } else { 82 } else {
77 throw new IllegalStateException("Query is asking for non-indexed key %s".formatted(relationViewKey)); 83 throw new IllegalStateException("Query is asking for non-indexed key %s".formatted(symbolViewKey));
78 } 84 }
79 } else { 85 } else {
80 throw new IllegalStateException("Query is asking for non-relational key"); 86 throw new IllegalStateException("Query is asking for non-relational key");
@@ -83,10 +89,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
83 89
84 @Override 90 @Override
85 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) { 91 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
86 var relationViewKey = checkKey(key); 92 Iterator<Object[]> iterator = enumerate(key, seedMask, seed).iterator();
87 Iterable<Object[]> allObjects = relationViewKey.getAll(model);
88 Iterable<Object[]> filteredBySeed = filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed));
89 Iterator<Object[]> iterator = filteredBySeed.iterator();
90 int result = 0; 93 int result = 0;
91 while (iterator.hasNext()) { 94 while (iterator.hasNext()) {
92 iterator.next(); 95 iterator.next();
@@ -102,13 +105,25 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
102 105
103 @Override 106 @Override
104 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) { 107 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
108 var filteredBySeed = enumerate(key, seedMask, seed);
109 return map(filteredBySeed, Tuples::flatTupleOf);
110 }
111
112 @Override
113 public Iterable<?> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
114 var index = seedMask.getFirstOmittedIndex().orElseThrow(
115 () -> new IllegalArgumentException("Seed mask does not omit a value"));
116 var filteredBySeed = enumerate(key, seedMask, seed);
117 return map(filteredBySeed, array -> array[index]);
118 }
119
120 private Iterable<Object[]> enumerate(IInputKey key, TupleMask seedMask, ITuple seed) {
105 var relationViewKey = checkKey(key); 121 var relationViewKey = checkKey(key);
106 Iterable<Object[]> allObjects = relationViewKey.getAll(model); 122 Iterable<Object[]> allObjects = relationViewKey.getAll(model);
107 Iterable<Object[]> filteredBySeed = filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed)); 123 return filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed));
108 return map(filteredBySeed, Tuples::flatTupleOf);
109 } 124 }
110 125
111 private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) { 126 private static boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) {
112 for (int i = 0; i < seedMask.indices.length; i++) { 127 for (int i = 0; i < seedMask.indices.length; i++) {
113 final Object seedElement = seed.get(i); 128 final Object seedElement = seed.get(i);
114 final Object tupleElement = tuple[seedMask.indices[i]]; 129 final Object tupleElement = tuple[seedMask.indices[i]];
@@ -120,11 +135,6 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
120 } 135 }
121 136
122 @Override 137 @Override
123 public Iterable<?> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
124 return enumerateTuples(key, seedMask, seed);
125 }
126
127 @Override
128 public boolean containsTuple(IInputKey key, ITuple seed) { 138 public boolean containsTuple(IInputKey key, ITuple seed) {
129 var relationViewKey = checkKey(key); 139 var relationViewKey = checkKey(key);
130 return relationViewKey.get(model, seed.getElements()); 140 return relationViewKey.get(model, seed.getElements());
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java
new file mode 100644
index 00000000..37177cbf
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/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 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.localsearch;
10
11import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
12import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
13import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor;
14
15import 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 * @since 2.0
21 */
22abstract class ExtendOperationExecutor<T> implements ISearchOperationExecutor {
23
24 private Iterator<? extends T> it;
25
26 /**
27 * Returns an iterator with the possible options from the current state
28 * @since 2.0
29 */
30 @SuppressWarnings("squid:S1452")
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 of {@link org.eclipse.viatra.query.runtime.localsearch.operations.ExtendOperationExecutor#execute}
61 * that handles failed unification of variables correctly.
62 * @param frame The matching frame to extend.
63 * @param context The search context.
64 * @return {@code true} if an extension was found, {@code false} otherwise.
65 */
66 @Override
67 public boolean execute(MatchingFrame frame, ISearchContext context) {
68 while (it.hasNext()) {
69 var newValue = it.next();
70 if (fillInValue(newValue, frame, context)) {
71 return true;
72 }
73 }
74 return false;
75 }
76}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
new file mode 100644
index 00000000..9d48c785
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
@@ -0,0 +1,117 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
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 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.localsearch;
10
11import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
12import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
13import org.eclipse.viatra.query.runtime.localsearch.operations.IPatternMatcherOperation;
14import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation;
15import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation;
16import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
17import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
18import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
19import org.eclipse.viatra.query.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
20
21import java.util.Iterator;
22import java.util.List;
23import java.util.function.Function;
24
25/**
26 * @author Grill Balázs
27 * @since 1.4
28 *
29 */
30public class ExtendPositivePatternCall implements ISearchOperation, IPatternMatcherOperation {
31
32 private class Executor extends ExtendOperationExecutor<Tuple> {
33 private final VolatileModifiableMaskedTuple maskedTuple;
34
35 public Executor() {
36 maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
37 }
38
39 @Override
40 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
41 maskedTuple.updateTuple(frame);
42 IQueryResultProvider matcher = context.getMatcher(information.getCallWithAdornment());
43 return matcher.getAllMatches(information.getParameterMask(), maskedTuple).iterator();
44 }
45
46 /**
47 * @since 2.0
48 */
49 @Override
50 protected boolean fillInValue(Tuple result, MatchingFrame frame, ISearchContext context) {
51 TupleMask mask = information.getFullFrameMask();
52 // The first loop clears out the elements from a possible previous iteration
53 for(int i : information.getFreeParameterIndices()) {
54 mask.set(frame, i, null);
55 }
56 for(int i : information.getFreeParameterIndices()) {
57 Object oldValue = mask.getValue(frame, i);
58 Object valueToFill = result.get(i);
59 if (oldValue != null && !oldValue.equals(valueToFill)){
60 // If the inverse map contains more than one values for the same key, it means that these arguments are unified by the caller.
61 // In this case if the callee assigns different values the frame shall be dropped
62 return false;
63 }
64 mask.set(frame, i, valueToFill);
65 }
66 return true;
67 }
68
69 @Override
70 protected void cleanup(MatchingFrame frame, ISearchContext context) {
71 TupleMask mask = information.getFullFrameMask();
72 for(int i : information.getFreeParameterIndices()){
73 mask.set(frame, i, null);
74 }
75
76 }
77
78 @Override
79 public ISearchOperation getOperation() {
80 return ExtendPositivePatternCall.this;
81 }
82 }
83
84 private final CallInformation information;
85
86 /**
87 * @since 1.7
88 */
89 public ExtendPositivePatternCall(CallInformation information) {
90 this.information = information;
91 }
92
93 @Override
94 public ISearchOperationExecutor createExecutor() {
95 return new Executor();
96 }
97
98 @Override
99 public List<Integer> getVariablePositions() {
100 return information.getVariablePositions();
101 }
102
103 @Override
104 public String toString() {
105 return toString(Object::toString);
106 }
107
108 @Override
109 public String toString(@SuppressWarnings("squid:S4276") Function<Integer, String> variableMapping) {
110 return "extend find " + information.toString(variableMapping);
111 }
112
113 @Override
114 public CallInformation getCallInformation() {
115 return information;
116 }
117}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
new file mode 100644
index 00000000..cc906f22
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
@@ -0,0 +1,35 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.localsearch;
7
8import org.eclipse.viatra.query.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
9import org.eclipse.viatra.query.runtime.localsearch.planner.cost.impl.StatisticsBasedConstraintCostFunction;
10import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
12import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
13import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
14
15import java.util.Optional;
16
17public class FlatCostFunction extends StatisticsBasedConstraintCostFunction {
18 public FlatCostFunction() {
19 // No inverse navigation penalty thanks to relational storage.
20 super(0);
21 }
22
23 @Override
24 public Optional<Long> projectionSize(IConstraintEvaluationContext input, IInputKey supplierKey, TupleMask groupMask, Accuracy requiredAccuracy) {
25 // We always start from an empty model, where every projection is of size 0.
26 // Therefore, projection size estimation is meaningless.
27 return Optional.empty();
28 }
29
30 @Override
31 protected double _calculateCost(TypeConstraint constraint, IConstraintEvaluationContext input) {
32 // Assume a flat cost for each relation. Maybe adjust in the future if we perform indexing?
33 return DEFAULT_COST;
34 }
35}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
new file mode 100644
index 00000000..96ac4a72
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
@@ -0,0 +1,137 @@
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 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package tools.refinery.store.query.viatra.internal.localsearch;
10
11import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
12import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
13import org.eclipse.viatra.query.runtime.localsearch.operations.IIteratingSearchOperation;
14import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation;
15import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
16import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
17import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
18import org.eclipse.viatra.query.runtime.matchers.tuple.VolatileMaskedTuple;
19import org.eclipse.viatra.query.runtime.matchers.util.Preconditions;
20
21import java.util.*;
22import java.util.function.Function;
23import java.util.stream.Collectors;
24
25/**
26 * @author Zoltan Ujhelyi
27 * @since 1.7
28 */
29public class GenericTypeExtend implements IIteratingSearchOperation {
30 private class Executor extends ExtendOperationExecutor<Tuple> {
31 private final VolatileMaskedTuple maskedTuple;
32
33 public Executor() {
34 this.maskedTuple = new VolatileMaskedTuple(callMask);
35 }
36
37 @Override
38 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
39 maskedTuple.updateTuple(frame);
40 return context.getRuntimeContext().enumerateTuples(type, indexerMask, maskedTuple).iterator();
41 }
42
43 @Override
44 protected boolean fillInValue(Tuple newTuple, MatchingFrame frame, ISearchContext context) {
45 for (Integer position : unboundVariableIndices) {
46 frame.setValue(position, null);
47 }
48 for (int i = 0; i < positions.length; i++) {
49 Object newValue = newTuple.get(i);
50 Object oldValue = frame.getValue(positions[i]);
51 if (oldValue != null && !Objects.equals(oldValue, newValue)) {
52 // If positions tuple maps more than one values for the same element (e.g. loop), it means that
53 // these arguments are to unified by the caller. In this case if the callee assigns different values
54 // the frame shall be considered a failed match
55 return false;
56 }
57 frame.setValue(positions[i], newValue);
58 }
59 return true;
60 }
61
62 @Override
63 protected void cleanup(MatchingFrame frame, ISearchContext context) {
64 for (Integer position : unboundVariableIndices) {
65 frame.setValue(position, null);
66 }
67 }
68
69 @Override
70 public ISearchOperation getOperation() {
71 return GenericTypeExtend.this;
72 }
73 }
74
75 private final IInputKey type;
76 private final int[] positions;
77 private final List<Integer> positionList;
78 private final Set<Integer> unboundVariableIndices;
79 private final TupleMask indexerMask;
80 private final TupleMask callMask;
81
82 /**
83 *
84 * @param type
85 * the type to execute the extend operation on
86 * @param positions
87 * the parameter positions that represent the variables of the input key
88 * @param unboundVariableIndices
89 * the set of positions that are bound at the start of the operation
90 */
91 public GenericTypeExtend(IInputKey type, int[] positions, TupleMask callMask, TupleMask indexerMask, Set<Integer> unboundVariableIndices) {
92 Preconditions.checkArgument(positions.length == type.getArity(),
93 "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(),
94 type.getArity(), positions.length);
95 List<Integer> modifiablePositionList = new ArrayList<>();
96 for (int position : positions) {
97 modifiablePositionList.add(position);
98 }
99 this.positionList = Collections.unmodifiableList(modifiablePositionList);
100 this.positions = positions;
101 this.type = type;
102
103 this.unboundVariableIndices = unboundVariableIndices;
104 this.indexerMask = indexerMask;
105 this.callMask = callMask;
106 }
107
108 @Override
109 public IInputKey getIteratedInputKey() {
110 return type;
111 }
112
113 @Override
114 public ISearchOperationExecutor createExecutor() {
115 return new Executor();
116 }
117
118 @Override
119 public List<Integer> getVariablePositions() {
120 return positionList;
121 }
122
123 @Override
124 public String toString() {
125 return toString(Object::toString);
126 }
127
128 @Override
129 public String toString(@SuppressWarnings("squid:S4276") Function<Integer, String> variableMapping) {
130 return "extend " + type.getPrettyPrintableName() + "("
131 + positionList.stream()
132 .map(input -> String.format("%s%s", unboundVariableIndices.contains(input) ? "-" : "+", variableMapping.apply(input)))
133 .collect(Collectors.joining(", "))
134 + ")";
135 }
136
137}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
new file mode 100644
index 00000000..0c77f587
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
@@ -0,0 +1,60 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.localsearch;
7
8import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
9import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend;
10import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints;
11import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider;
12import org.eclipse.viatra.query.runtime.localsearch.plan.SimplePlanProvider;
13import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability;
14import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
15import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
16import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
17import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
18import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
19
20public class RelationalLocalSearchBackendFactory implements IQueryBackendFactory {
21 public static final RelationalLocalSearchBackendFactory INSTANCE = new RelationalLocalSearchBackendFactory();
22
23 private RelationalLocalSearchBackendFactory() {
24 }
25
26 @Override
27 public IQueryBackend create(IQueryBackendContext context) {
28 return new LocalSearchBackend(context) {
29 // Create a new {@link IPlanProvider}, because the original {@link LocalSearchBackend#planProvider} is not
30 // accessible.
31 private final IPlanProvider planProvider = new SimplePlanProvider(context.getLogger());
32
33 @Override
34 protected AbstractLocalSearchResultProvider initializeResultProvider(PQuery query,
35 QueryEvaluationHint hints) {
36 return new RelationalLocalSearchResultProvider(this, context, query, planProvider, hints);
37 }
38
39 @Override
40 public IQueryBackendFactory getFactory() {
41 return RelationalLocalSearchBackendFactory.this;
42 }
43 };
44 }
45
46 @Override
47 public Class<? extends IQueryBackend> getBackendClass() {
48 return LocalSearchBackend.class;
49 }
50
51 @Override
52 public IMatcherCapability calculateRequiredCapability(PQuery pQuery, QueryEvaluationHint queryEvaluationHint) {
53 return LocalSearchHints.parse(queryEvaluationHint);
54 }
55
56 @Override
57 public boolean isCaching() {
58 return false;
59 }
60}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
new file mode 100644
index 00000000..da37be14
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
@@ -0,0 +1,28 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.localsearch;
7
8import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
9import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend;
10import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints;
11import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider;
12import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.IOperationCompiler;
13import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
14import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
15import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
16
17class RelationalLocalSearchResultProvider extends AbstractLocalSearchResultProvider {
18 public RelationalLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
19 IPlanProvider planProvider, QueryEvaluationHint userHints) {
20 super(backend, context, query, planProvider, userHints);
21 }
22
23 @Override
24 protected IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext,
25 LocalSearchHints configuration) {
26 return new RelationalOperationCompiler(runtimeContext);
27 }
28}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
new file mode 100644
index 00000000..f76ef486
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
@@ -0,0 +1,70 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.localsearch;
7
8import org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue;
9import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation;
10import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.GenericOperationCompiler;
11import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
12import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
13import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
14import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
16import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
17import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
18
19import java.util.*;
20
21public class RelationalOperationCompiler extends GenericOperationCompiler {
22 public RelationalOperationCompiler(IQueryRuntimeContext runtimeContext) {
23 super(runtimeContext);
24 }
25
26 @Override
27 protected void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
28 IInputKey inputKey = typeConstraint.getSupplierKey();
29 Tuple tuple = typeConstraint.getVariablesTuple();
30
31 int[] positions = new int[tuple.getSize()];
32 List<Integer> boundVariableIndices = new ArrayList<>();
33 List<Integer> boundVariables = new ArrayList<>();
34 Set<Integer> unboundVariables = new HashSet<>();
35 for (int i = 0; i < tuple.getSize(); i++) {
36 PVariable variable = (PVariable) tuple.get(i);
37 Integer position = variableMapping.get(variable);
38 positions[i] = position;
39 if (variableBindings.get(typeConstraint).contains(position)) {
40 boundVariableIndices.add(i);
41 boundVariables.add(position);
42 } else {
43 unboundVariables.add(position);
44 }
45 }
46 TupleMask indexerMask = TupleMask.fromSelectedIndices(inputKey.getArity(), boundVariableIndices);
47 TupleMask callMask = TupleMask.fromSelectedIndices(variableMapping.size(), boundVariables);
48 // If multiple tuple elements from the indexer should be bound to the same variable, we must use a
49 // {@link GenericTypeExtend} check whether the tuple elements have the same value.
50 if (unboundVariables.size() == 1 && indexerMask.getSize() + 1 == indexerMask.getSourceWidth()) {
51 operations.add(new GenericTypeExtendSingleValue(inputKey, positions, callMask, indexerMask,
52 unboundVariables.iterator().next()));
53 } else {
54 // Use a fixed version of
55 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtend} that handles
56 // failed unification of variables correctly.
57 operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables));
58 }
59 }
60
61 @Override
62 protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
63 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
64 // Use a fixed version of
65 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.extend.ExtendPositivePatternCall} that handles
66 // failed unification of variables correctly.
67 operations.add(new ExtendPositivePatternCall(information));
68 dependencies.add(information.getCallWithAdornment());
69 }
70}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java
new file mode 100644
index 00000000..99b0a3d8
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/AbstractViatraMatcher.java
@@ -0,0 +1,32 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
9import org.eclipse.viatra.query.runtime.matchers.backend.IUpdateable;
10import tools.refinery.store.query.dnf.Query;
11import tools.refinery.store.query.resultset.AbstractResultSet;
12import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
13
14public abstract class AbstractViatraMatcher<T> extends AbstractResultSet<T> implements IUpdateable {
15 protected final IQueryResultProvider backend;
16
17 protected AbstractViatraMatcher(ViatraModelQueryAdapterImpl adapter, Query<T> query,
18 RawPatternMatcher rawPatternMatcher) {
19 super(adapter, query);
20 backend = rawPatternMatcher.getBackend();
21 }
22
23 @Override
24 protected void startListeningForChanges() {
25 backend.addUpdateListener(this, this, false);
26 }
27
28 @Override
29 protected void stopListeningForChanges() {
30 backend.removeUpdateListener(this);
31 }
32}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
new file mode 100644
index 00000000..47efb2aa
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
@@ -0,0 +1,52 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer;
9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.tuple.Tuple;
11
12import java.util.Iterator;
13
14class FunctionalCursor<T> implements Cursor<Tuple, T> {
15 private final IterableIndexer indexer;
16 private final Iterator<org.eclipse.viatra.query.runtime.matchers.tuple.Tuple> iterator;
17 private boolean terminated;
18 private Tuple key;
19 private T value;
20
21 public FunctionalCursor(IterableIndexer indexer) {
22 this.indexer = indexer;
23 iterator = indexer.getSignatures().iterator();
24 }
25
26 @Override
27 public Tuple getKey() {
28 return key;
29 }
30
31 @Override
32 public T getValue() {
33 return value;
34 }
35
36 @Override
37 public boolean isTerminated() {
38 return terminated;
39 }
40
41 @Override
42 public boolean move() {
43 if (!terminated && iterator.hasNext()) {
44 var match = iterator.next();
45 key = MatcherUtils.toRefineryTuple(match);
46 value = MatcherUtils.getSingleValue(indexer.get(match));
47 return true;
48 }
49 terminated = true;
50 return false;
51 }
52}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
new file mode 100644
index 00000000..db4740cd
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
@@ -0,0 +1,88 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
10import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
12import tools.refinery.store.map.Cursor;
13import tools.refinery.store.query.dnf.FunctionalQuery;
14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
15import tools.refinery.store.tuple.Tuple;
16
17/**
18 * Directly access the tuples inside a VIATRA pattern matcher.<p>
19 * This class neglects calling
20 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
21 * and
22 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
23 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
24 * implementation for these methods.
25 * Using this class with any other runtime context may lead to undefined behavior.
26 */
27public class FunctionalViatraMatcher<T> extends AbstractViatraMatcher<T> {
28 private final TupleMask emptyMask;
29 private final TupleMask omitOutputMask;
30 private final IterableIndexer omitOutputIndexer;
31
32 public FunctionalViatraMatcher(ViatraModelQueryAdapterImpl adapter, FunctionalQuery<T> query,
33 RawPatternMatcher rawPatternMatcher) {
34 super(adapter, query, rawPatternMatcher);
35 int arity = query.arity();
36 int arityWithOutput = arity + 1;
37 emptyMask = TupleMask.empty(arityWithOutput);
38 omitOutputMask = TupleMask.omit(arity, arityWithOutput);
39 if (backend instanceof RetePatternMatcher reteBackend) {
40 var maybeIterableOmitOutputIndexer = IndexerUtils.getIndexer(reteBackend, omitOutputMask);
41 if (maybeIterableOmitOutputIndexer instanceof IterableIndexer iterableOmitOutputIndexer) {
42 omitOutputIndexer = iterableOmitOutputIndexer;
43 } else {
44 omitOutputIndexer = null;
45 }
46 } else {
47 omitOutputIndexer = null;
48 }
49 }
50
51 @Override
52 public T get(Tuple parameters) {
53 var tuple = MatcherUtils.toViatraTuple(parameters);
54 if (omitOutputIndexer == null) {
55 return MatcherUtils.getSingleValue(backend.getAllMatches(omitOutputMask, tuple).iterator());
56 } else {
57 return MatcherUtils.getSingleValue(omitOutputIndexer.get(tuple));
58 }
59 }
60
61 @Override
62 public Cursor<Tuple, T> getAll() {
63 if (omitOutputIndexer == null) {
64 var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf());
65 return new UnsafeFunctionalCursor<>(allMatches.iterator());
66 }
67 return new FunctionalCursor<>(omitOutputIndexer);
68 }
69
70 @Override
71 public int size() {
72 if (omitOutputIndexer == null) {
73 return backend.countMatches(emptyMask, Tuples.staticArityFlatTupleOf());
74 }
75 return omitOutputIndexer.getBucketCount();
76 }
77
78 @Override
79 public void update(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple updateElement, boolean isInsertion) {
80 var key = MatcherUtils.keyToRefineryTuple(updateElement);
81 var value = MatcherUtils.<T>getValue(updateElement);
82 if (isInsertion) {
83 notifyChange(key, null, value);
84 } else {
85 notifyChange(key, value, null);
86 }
87 }
88}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
new file mode 100644
index 00000000..15f00b2d
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
@@ -0,0 +1,53 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.rete.index.Indexer;
10import org.eclipse.viatra.query.runtime.rete.matcher.ReteEngine;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
12import org.eclipse.viatra.query.runtime.rete.traceability.RecipeTraceInfo;
13
14import java.lang.invoke.MethodHandle;
15import java.lang.invoke.MethodHandles;
16import java.lang.invoke.MethodType;
17
18final class IndexerUtils {
19 private static final MethodHandle GET_ENGINE_HANDLE;
20 private static final MethodHandle GET_PRODUCTION_NODE_TRACE_HANDLE;
21 private static final MethodHandle ACCESS_PROJECTION_HANDLE;
22
23 static {
24 try {
25 var lookup = MethodHandles.privateLookupIn(RetePatternMatcher.class, MethodHandles.lookup());
26 GET_ENGINE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "engine", ReteEngine.class);
27 GET_PRODUCTION_NODE_TRACE_HANDLE = lookup.findGetter(RetePatternMatcher.class, "productionNodeTrace",
28 RecipeTraceInfo.class);
29 ACCESS_PROJECTION_HANDLE = lookup.findVirtual(ReteEngine.class, "accessProjection",
30 MethodType.methodType(Indexer.class, RecipeTraceInfo.class, TupleMask.class));
31 } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) {
32 throw new IllegalStateException("Cannot access private members of %s"
33 .formatted(RetePatternMatcher.class.getPackageName()), e);
34 }
35 }
36
37 private IndexerUtils() {
38 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
39 }
40
41 public static Indexer getIndexer(RetePatternMatcher backend, TupleMask mask) {
42 try {
43 var engine = (ReteEngine) GET_ENGINE_HANDLE.invokeExact(backend);
44 var trace = (RecipeTraceInfo) GET_PRODUCTION_NODE_TRACE_HANDLE.invokeExact(backend);
45 return (Indexer) ACCESS_PROJECTION_HANDLE.invokeExact(engine, trace, mask);
46 } catch (Error e) {
47 // Fatal JVM errors should not be wrapped.
48 throw e;
49 } catch (Throwable e) {
50 throw new IllegalStateException("Cannot access matcher for mask " + mask, e);
51 }
52 }
53}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
new file mode 100644
index 00000000..6e24812a
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
@@ -0,0 +1,115 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
10import org.jetbrains.annotations.Nullable;
11import tools.refinery.store.tuple.*;
12
13import java.util.Iterator;
14
15final class MatcherUtils {
16 private MatcherUtils() {
17 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
18 }
19
20 public static org.eclipse.viatra.query.runtime.matchers.tuple.Tuple toViatraTuple(Tuple refineryTuple) {
21 if (refineryTuple instanceof Tuple0) {
22 return Tuples.staticArityFlatTupleOf();
23 } else if (refineryTuple instanceof Tuple1) {
24 return Tuples.staticArityFlatTupleOf(refineryTuple);
25 } else if (refineryTuple instanceof Tuple2 tuple2) {
26 return Tuples.staticArityFlatTupleOf(Tuple.of(tuple2.value0()), Tuple.of(tuple2.value1()));
27 } else if (refineryTuple instanceof Tuple3 tuple3) {
28 return Tuples.staticArityFlatTupleOf(Tuple.of(tuple3.value0()), Tuple.of(tuple3.value1()),
29 Tuple.of(tuple3.value2()));
30 } else if (refineryTuple instanceof Tuple4 tuple4) {
31 return Tuples.staticArityFlatTupleOf(Tuple.of(tuple4.value0()), Tuple.of(tuple4.value1()),
32 Tuple.of(tuple4.value2()), Tuple.of(tuple4.value3()));
33 } else {
34 int arity = refineryTuple.getSize();
35 var values = new Object[arity];
36 for (int i = 0; i < arity; i++) {
37 values[i] = Tuple.of(refineryTuple.get(i));
38 }
39 return Tuples.flatTupleOf(values);
40 }
41 }
42
43 public static Tuple toRefineryTuple(ITuple viatraTuple) {
44 int arity = viatraTuple.getSize();
45 if (arity == 1) {
46 return getWrapper(viatraTuple, 0);
47 }
48 return prefixToRefineryTuple(viatraTuple, viatraTuple.getSize());
49 }
50
51 public static Tuple keyToRefineryTuple(ITuple viatraTuple) {
52 return prefixToRefineryTuple(viatraTuple, viatraTuple.getSize() - 1);
53 }
54
55 private static Tuple prefixToRefineryTuple(ITuple viatraTuple, int targetArity) {
56 if (targetArity < 0) {
57 throw new IllegalArgumentException("Requested negative prefix %d of %s"
58 .formatted(targetArity, viatraTuple));
59 }
60 return switch (targetArity) {
61 case 0 -> Tuple.of();
62 case 1 -> Tuple.of(unwrap(viatraTuple, 0));
63 case 2 -> Tuple.of(unwrap(viatraTuple, 0), unwrap(viatraTuple, 1));
64 case 3 -> Tuple.of(unwrap(viatraTuple, 0), unwrap(viatraTuple, 1), unwrap(viatraTuple, 2));
65 case 4 -> Tuple.of(unwrap(viatraTuple, 0), unwrap(viatraTuple, 1), unwrap(viatraTuple, 2),
66 unwrap(viatraTuple, 3));
67 default -> {
68 var entries = new int[targetArity];
69 for (int i = 0; i < targetArity; i++) {
70 entries[i] = unwrap(viatraTuple, i);
71 }
72 yield Tuple.of(entries);
73 }
74 };
75 }
76
77 private static Tuple1 getWrapper(ITuple viatraTuple, int index) {
78 if (!((viatraTuple.get(index)) instanceof Tuple1 wrappedObjectId)) {
79 throw new IllegalArgumentException("Element %d of tuple %s is not an object id"
80 .formatted(index, viatraTuple));
81 }
82 return wrappedObjectId;
83 }
84
85 private static int unwrap(ITuple viatraTuple, int index) {
86 return getWrapper(viatraTuple, index).value0();
87 }
88
89 public static <T> T getValue(ITuple match) {
90 // This is only safe if we know for sure that match came from a functional query of type {@code T}.
91 @SuppressWarnings("unchecked")
92 var result = (T) match.get(match.getSize() - 1);
93 return result;
94 }
95
96 public static <T> T getSingleValue(@Nullable Iterable<? extends ITuple> viatraTuples) {
97 if (viatraTuples == null) {
98 return null;
99 }
100 return getSingleValue(viatraTuples.iterator());
101 }
102
103 public static <T> T getSingleValue(Iterator<? extends ITuple> iterator) {
104 if (!iterator.hasNext()) {
105 return null;
106 }
107 var match = iterator.next();
108 var result = MatcherUtils.<T>getValue(match);
109 if (iterator.hasNext()) {
110 var input = keyToRefineryTuple(match);
111 throw new IllegalStateException("Query is not functional for input tuple: " + input);
112 }
113 return result;
114 }
115}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
new file mode 100644
index 00000000..5b82c4b7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
@@ -0,0 +1,20 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
9import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
10import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
11
12public class RawPatternMatcher extends GenericPatternMatcher {
13 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
14 super(specification);
15 }
16
17 IQueryResultProvider getBackend() {
18 return backend;
19 }
20}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
new file mode 100644
index 00000000..1dc8f5db
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
@@ -0,0 +1,47 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.tuple.Tuple;
11
12import java.util.Iterator;
13
14class RelationalCursor implements Cursor<Tuple, Boolean> {
15 private final Iterator<? extends ITuple> tuplesIterator;
16 private boolean terminated;
17 private Tuple key;
18
19 public RelationalCursor(Iterator<? extends ITuple> tuplesIterator) {
20 this.tuplesIterator = tuplesIterator;
21 }
22
23 @Override
24 public Tuple getKey() {
25 return key;
26 }
27
28 @Override
29 public Boolean getValue() {
30 return true;
31 }
32
33 @Override
34 public boolean isTerminated() {
35 return terminated;
36 }
37
38 @Override
39 public boolean move() {
40 if (!terminated && tuplesIterator.hasNext()) {
41 key = MatcherUtils.toRefineryTuple(tuplesIterator.next());
42 return true;
43 }
44 terminated = true;
45 return false;
46 }
47}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
new file mode 100644
index 00000000..ac95dcc0
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
@@ -0,0 +1,80 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
10import org.eclipse.viatra.query.runtime.rete.index.Indexer;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
12import tools.refinery.store.map.Cursor;
13import tools.refinery.store.map.Cursors;
14import tools.refinery.store.query.dnf.RelationalQuery;
15import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
16import tools.refinery.store.tuple.Tuple;
17
18/**
19 * Directly access the tuples inside a VIATRA pattern matcher.<p>
20 * This class neglects calling
21 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
22 * and
23 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
24 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
25 * implementation for these methods.
26 * Using this class with any other runtime context may lead to undefined behavior.
27 */
28public class RelationalViatraMatcher extends AbstractViatraMatcher<Boolean> {
29 private final TupleMask emptyMask;
30 private final TupleMask identityMask;
31 private final Indexer emptyMaskIndexer;
32
33 public RelationalViatraMatcher(ViatraModelQueryAdapterImpl adapter, RelationalQuery query,
34 RawPatternMatcher rawPatternMatcher) {
35 super(adapter, query, rawPatternMatcher);
36 int arity = query.arity();
37 emptyMask = TupleMask.empty(arity);
38 identityMask = TupleMask.identity(arity);
39 if (backend instanceof RetePatternMatcher reteBackend) {
40 emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, emptyMask);
41 } else {
42 emptyMaskIndexer = null;
43 }
44 }
45
46 @Override
47 public Boolean get(Tuple parameters) {
48 var tuple = MatcherUtils.toViatraTuple(parameters);
49 if (emptyMaskIndexer == null) {
50 return backend.hasMatch(identityMask, tuple);
51 }
52 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
53 return matches != null && matches.contains(tuple);
54 }
55
56 @Override
57 public Cursor<Tuple, Boolean> getAll() {
58 if (emptyMaskIndexer == null) {
59 var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf());
60 return new RelationalCursor(allMatches.iterator());
61 }
62 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
63 return matches == null ? Cursors.empty() : new RelationalCursor(matches.stream().iterator());
64 }
65
66 @Override
67 public int size() {
68 if (emptyMaskIndexer == null) {
69 return backend.countMatches(emptyMask, Tuples.staticArityFlatTupleOf());
70 }
71 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
72 return matches == null ? 0 : matches.size();
73 }
74
75 @Override
76 public void update(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple updateElement, boolean isInsertion) {
77 var key = MatcherUtils.toRefineryTuple(updateElement);
78 notifyChange(key, !isInsertion, isInsertion);
79 }
80}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
new file mode 100644
index 00000000..b0b507fe
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
@@ -0,0 +1,55 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.matcher;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
9import tools.refinery.store.map.Cursor;
10import tools.refinery.store.tuple.Tuple;
11
12import java.util.Iterator;
13
14/**
15 * Cursor for a functional result set that iterates over a stream of raw matches and doesn't check whether the
16 * functional dependency of the output on the inputs is obeyed.
17 * @param <T> The output type.
18 */
19class UnsafeFunctionalCursor<T> implements Cursor<Tuple, T> {
20 private final Iterator<? extends ITuple> tuplesIterator;
21 private boolean terminated;
22 private Tuple key;
23 private T value;
24
25 public UnsafeFunctionalCursor(Iterator<? extends ITuple> tuplesIterator) {
26 this.tuplesIterator = tuplesIterator;
27 }
28
29 @Override
30 public Tuple getKey() {
31 return key;
32 }
33
34 @Override
35 public T getValue() {
36 return value;
37 }
38
39 @Override
40 public boolean isTerminated() {
41 return terminated;
42 }
43
44 @Override
45 public boolean move() {
46 if (!terminated && tuplesIterator.hasNext()) {
47 var match = tuplesIterator.next();
48 key = MatcherUtils.keyToRefineryTuple(match);
49 value = MatcherUtils.getValue(match);
50 return true;
51 }
52 terminated = true;
53 return false;
54 }
55}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java
new file mode 100644
index 00000000..cf127291
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java
@@ -0,0 +1,21 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery;
7
8import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
9import tools.refinery.store.query.term.Term;
10
11class AssumptionEvaluator extends TermEvaluator<Boolean> {
12 public AssumptionEvaluator(Term<Boolean> term) {
13 super(term);
14 }
15
16 @Override
17 public Object evaluateExpression(IValueProvider provider) {
18 var result = super.evaluateExpression(provider);
19 return result == null ? Boolean.FALSE : result;
20 }
21}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
deleted file mode 100644
index 60f1bcae..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java
+++ /dev/null
@@ -1,223 +0,0 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
5import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
6import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
7import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation;
8import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality;
9import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue;
14import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
16import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
17import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
19import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
20import tools.refinery.store.query.DNF;
21import tools.refinery.store.query.DNFAnd;
22import tools.refinery.store.query.DNFUtils;
23import tools.refinery.store.query.Variable;
24import tools.refinery.store.query.atom.*;
25import tools.refinery.store.query.view.AnyRelationView;
26
27import java.util.*;
28import java.util.function.Function;
29import java.util.stream.Collectors;
30
31public class DNF2PQuery {
32 private static final Object P_CONSTRAINT_LOCK = new Object();
33
34 private final Set<DNF> translating = new LinkedHashSet<>();
35
36 private final Map<DNF, RawPQuery> dnf2PQueryMap = new HashMap<>();
37
38 private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>();
39
40 private final Map<AnyRelationView, RawPQuery> view2EmbeddedMap = new HashMap<>();
41
42 private Function<DNF, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null,
43 QueryEvaluationHint.BackendRequirement.UNSPECIFIED);
44
45 public void setComputeHint(Function<DNF, QueryEvaluationHint> computeHint) {
46 this.computeHint = computeHint;
47 }
48
49 public RawPQuery translate(DNF dnfQuery) {
50 if (translating.contains(dnfQuery)) {
51 var path = translating.stream().map(DNF::name).collect(Collectors.joining(" -> "));
52 throw new IllegalStateException("Circular reference %s -> %s detected".formatted(path,
53 dnfQuery.name()));
54 }
55 // We can't use computeIfAbsent here, because translating referenced queries calls this method in a reentrant
56 // way, which would cause a ConcurrentModificationException with computeIfAbsent.
57 var pQuery = dnf2PQueryMap.get(dnfQuery);
58 if (pQuery == null) {
59 translating.add(dnfQuery);
60 try {
61 pQuery = doTranslate(dnfQuery);
62 dnf2PQueryMap.put(dnfQuery, pQuery);
63 } finally {
64 translating.remove(dnfQuery);
65 }
66 }
67 return pQuery;
68 }
69
70 public Map<AnyRelationView, IInputKey> getRelationViews() {
71 return Collections.unmodifiableMap(view2WrapperMap);
72 }
73
74 public RawPQuery getAlreadyTranslated(DNF dnfQuery) {
75 return dnf2PQueryMap.get(dnfQuery);
76 }
77
78 private RawPQuery doTranslate(DNF dnfQuery) {
79 var pQuery = new RawPQuery(dnfQuery.getUniqueName());
80 pQuery.setEvaluationHints(computeHint.apply(dnfQuery));
81
82 Map<Variable, PParameter> parameters = new HashMap<>();
83 for (Variable variable : dnfQuery.getParameters()) {
84 parameters.put(variable, new PParameter(variable.getUniqueName()));
85 }
86
87 List<PParameter> parameterList = new ArrayList<>();
88 for (var param : dnfQuery.getParameters()) {
89 parameterList.add(parameters.get(param));
90 }
91 pQuery.setParameters(parameterList);
92
93 for (var functionalDependency : dnfQuery.getFunctionalDependencies()) {
94 var functionalDependencyAnnotation = new PAnnotation("FunctionalDependency");
95 for (var forEachVariable : functionalDependency.forEach()) {
96 functionalDependencyAnnotation.addAttribute("forEach", forEachVariable.getUniqueName());
97 }
98 for (var uniqueVariable : functionalDependency.unique()) {
99 functionalDependencyAnnotation.addAttribute("unique", uniqueVariable.getUniqueName());
100 }
101 pQuery.addAnnotation(functionalDependencyAnnotation);
102 }
103
104 // The constructor of {@link org.eclipse.viatra.query.runtime.matchers.psystem.BasePConstraint} mutates
105 // global static state (<code>nextID</code>) without locking. Therefore, we need to synchronize before creating
106 // any query constraints to avoid a data race.
107 synchronized (P_CONSTRAINT_LOCK) {
108 for (DNFAnd clause : dnfQuery.getClauses()) {
109 PBody body = new PBody(pQuery);
110 List<ExportedParameter> symbolicParameters = new ArrayList<>();
111 for (var param : dnfQuery.getParameters()) {
112 PVariable pVar = body.getOrCreateVariableByName(param.getUniqueName());
113 symbolicParameters.add(new ExportedParameter(body, pVar, parameters.get(param)));
114 }
115 body.setSymbolicParameters(symbolicParameters);
116 pQuery.addBody(body);
117 for (DNFAtom constraint : clause.constraints()) {
118 translateDNFAtom(constraint, body);
119 }
120 }
121 }
122
123 return pQuery;
124 }
125
126 private void translateDNFAtom(DNFAtom constraint, PBody body) {
127 if (constraint instanceof EquivalenceAtom equivalenceAtom) {
128 translateEquivalenceAtom(equivalenceAtom, body);
129 } else if (constraint instanceof RelationViewAtom relationViewAtom) {
130 translateRelationViewAtom(relationViewAtom, body);
131 } else if (constraint instanceof DNFCallAtom callAtom) {
132 translateCallAtom(callAtom, body);
133 } else if (constraint instanceof ConstantAtom constantAtom) {
134 translateConstantAtom(constantAtom, body);
135 } else {
136 throw new IllegalArgumentException("Unknown constraint: " + constraint.toString());
137 }
138 }
139
140 private void translateEquivalenceAtom(EquivalenceAtom equivalence, PBody body) {
141 PVariable varSource = body.getOrCreateVariableByName(equivalence.left().getUniqueName());
142 PVariable varTarget = body.getOrCreateVariableByName(equivalence.right().getUniqueName());
143 if (equivalence.positive()) {
144 new Equality(body, varSource, varTarget);
145 } else {
146 new Inequality(body, varSource, varTarget);
147 }
148 }
149
150 private void translateRelationViewAtom(RelationViewAtom relationViewAtom, PBody body) {
151 var substitution = translateSubstitution(relationViewAtom.getSubstitution(), body);
152 var polarity = relationViewAtom.getPolarity();
153 var relationView = relationViewAtom.getTarget();
154 if (polarity == CallPolarity.POSITIVE) {
155 new TypeConstraint(body, substitution, wrapView(relationView));
156 } else {
157 var embeddedPQuery = translateEmbeddedRelationViewPQuery(relationView);
158 switch (polarity) {
159 case TRANSITIVE -> new BinaryTransitiveClosure(body, substitution, embeddedPQuery);
160 case NEGATIVE -> new NegativePatternCall(body, substitution, embeddedPQuery);
161 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
162 }
163 }
164 }
165
166 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) {
167 int arity = substitution.size();
168 Object[] variables = new Object[arity];
169 for (int i = 0; i < arity; i++) {
170 var variable = substitution.get(i);
171 variables[i] = body.getOrCreateVariableByName(variable.getUniqueName());
172 }
173 return Tuples.flatTupleOf(variables);
174 }
175
176 private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) {
177 return view2EmbeddedMap.computeIfAbsent(relationView, this::doTranslateEmbeddedRelationViewPQuery);
178 }
179
180 private RawPQuery doTranslateEmbeddedRelationViewPQuery(AnyRelationView relationView) {
181 var embeddedPQuery = new RawPQuery(DNFUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED);
182 var body = new PBody(embeddedPQuery);
183 int arity = relationView.arity();
184 var parameters = new ArrayList<PParameter>(arity);
185 var arguments = new Object[arity];
186 var symbolicParameters = new ArrayList<ExportedParameter>(arity);
187 for (int i = 0; i < arity; i++) {
188 var parameterName = "p" + i;
189 var parameter = new PParameter(parameterName);
190 parameters.add(parameter);
191 var variable = body.getOrCreateVariableByName(parameterName);
192 arguments[i] = variable;
193 symbolicParameters.add(new ExportedParameter(body, variable, parameter));
194 }
195 embeddedPQuery.setParameters(parameters);
196 body.setSymbolicParameters(symbolicParameters);
197 var argumentTuple = Tuples.flatTupleOf(arguments);
198 new TypeConstraint(body, argumentTuple, wrapView(relationView));
199 embeddedPQuery.addBody(body);
200 return embeddedPQuery;
201 }
202
203 private RelationViewWrapper wrapView(AnyRelationView relationView) {
204 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new);
205 }
206
207 private void translateCallAtom(DNFCallAtom callAtom, PBody body) {
208 var variablesTuple = translateSubstitution(callAtom.getSubstitution(), body);
209 var translatedReferred = translate(callAtom.getTarget());
210 var polarity = callAtom.getPolarity();
211 switch (polarity) {
212 case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred);
213 case TRANSITIVE -> new BinaryTransitiveClosure(body, variablesTuple, translatedReferred);
214 case NEGATIVE -> new NegativePatternCall(body, variablesTuple, translatedReferred);
215 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
216 }
217 }
218
219 private void translateConstantAtom(ConstantAtom constantAtom, PBody body) {
220 var variable = body.getOrCreateVariableByName(constantAtom.variable().getUniqueName());
221 new ConstantValue(body, variable, constantAtom.nodeId());
222 }
223}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
new file mode 100644
index 00000000..5b0ea61d
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
@@ -0,0 +1,266 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery;
7
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
9import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
10import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
11import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
12import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
13import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator;
14import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
15import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation;
16import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.*;
17import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
18import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue;
19import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
20import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
21import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
22import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameterDirection;
23import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
24import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
25import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
26import tools.refinery.store.query.dnf.Dnf;
27import tools.refinery.store.query.dnf.DnfClause;
28import tools.refinery.store.query.dnf.SymbolicParameter;
29import tools.refinery.store.query.literal.*;
30import tools.refinery.store.query.term.ConstantTerm;
31import tools.refinery.store.query.term.StatefulAggregator;
32import tools.refinery.store.query.term.StatelessAggregator;
33import tools.refinery.store.query.term.Variable;
34import tools.refinery.store.query.view.AnySymbolView;
35import tools.refinery.store.util.CycleDetectingMapper;
36
37import java.util.*;
38import java.util.function.Function;
39import java.util.stream.Collectors;
40
41public class Dnf2PQuery {
42 private static final Object P_CONSTRAINT_LOCK = new Object();
43 private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name,
44 this::doTranslate);
45 private final QueryWrapperFactory wrapperFactory = new QueryWrapperFactory(this);
46 private final Map<Dnf, QueryEvaluationHint> hintOverrides = new LinkedHashMap<>();
47 private Function<Dnf, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null,
48 (IQueryBackendFactory) null);
49
50 public void setComputeHint(Function<Dnf, QueryEvaluationHint> computeHint) {
51 this.computeHint = computeHint;
52 }
53
54 public RawPQuery translate(Dnf dnfQuery) {
55 return mapper.map(dnfQuery);
56 }
57
58 public Map<AnySymbolView, IInputKey> getSymbolViews() {
59 return wrapperFactory.getSymbolViews();
60 }
61
62 public void hint(Dnf dnf, QueryEvaluationHint hint) {
63 hintOverrides.compute(dnf, (ignoredKey, existingHint) ->
64 existingHint == null ? hint : existingHint.overrideBy(hint));
65 }
66
67 private QueryEvaluationHint consumeHint(Dnf dnf) {
68 var defaultHint = computeHint.apply(dnf);
69 var existingHint = hintOverrides.remove(dnf);
70 return defaultHint.overrideBy(existingHint);
71 }
72
73 public void assertNoUnusedHints() {
74 if (hintOverrides.isEmpty()) {
75 return;
76 }
77 var unusedHints = hintOverrides.keySet().stream().map(Dnf::name).collect(Collectors.joining(", "));
78 throw new IllegalStateException(
79 "Unused query evaluation hints for %s. Hints must be set before a query is added to the engine"
80 .formatted(unusedHints));
81 }
82
83 private RawPQuery doTranslate(Dnf dnfQuery) {
84 var pQuery = new RawPQuery(dnfQuery.getUniqueName());
85 pQuery.setEvaluationHints(consumeHint(dnfQuery));
86
87 Map<SymbolicParameter, PParameter> parameters = new HashMap<>();
88 List<PParameter> parameterList = new ArrayList<>();
89 for (var parameter : dnfQuery.getSymbolicParameters()) {
90 var direction = switch (parameter.getDirection()) {
91 case OUT -> parameter.isUnifiable() ? PParameterDirection.INOUT : PParameterDirection.OUT;
92 case IN -> throw new IllegalArgumentException("Query %s with input parameter %s is not supported"
93 .formatted(dnfQuery, parameter.getVariable()));
94 };
95 var pParameter = new PParameter(parameter.getVariable().getUniqueName(), null, null, direction);
96 parameters.put(parameter, pParameter);
97 parameterList.add(pParameter);
98 }
99
100 pQuery.setParameters(parameterList);
101
102 for (var functionalDependency : dnfQuery.getFunctionalDependencies()) {
103 var functionalDependencyAnnotation = new PAnnotation("FunctionalDependency");
104 for (var forEachVariable : functionalDependency.forEach()) {
105 functionalDependencyAnnotation.addAttribute("forEach", forEachVariable.getUniqueName());
106 }
107 for (var uniqueVariable : functionalDependency.unique()) {
108 functionalDependencyAnnotation.addAttribute("unique", uniqueVariable.getUniqueName());
109 }
110 pQuery.addAnnotation(functionalDependencyAnnotation);
111 }
112
113 // The constructor of {@link org.eclipse.viatra.query.runtime.matchers.psystem.BasePConstraint} mutates
114 // global static state (<code>nextID</code>) without locking. Therefore, we need to synchronize before creating
115 // any query literals to avoid a data race.
116 synchronized (P_CONSTRAINT_LOCK) {
117 for (DnfClause clause : dnfQuery.getClauses()) {
118 PBody body = new PBody(pQuery);
119 List<ExportedParameter> parameterExports = new ArrayList<>();
120 for (var parameter : dnfQuery.getSymbolicParameters()) {
121 PVariable pVar = body.getOrCreateVariableByName(parameter.getVariable().getUniqueName());
122 parameterExports.add(new ExportedParameter(body, pVar, parameters.get(parameter)));
123 }
124 body.setSymbolicParameters(parameterExports);
125 pQuery.addBody(body);
126 for (Literal literal : clause.literals()) {
127 translateLiteral(literal, body);
128 }
129 }
130 }
131
132 return pQuery;
133 }
134
135 private void translateLiteral(Literal literal, PBody body) {
136 if (literal instanceof EquivalenceLiteral equivalenceLiteral) {
137 translateEquivalenceLiteral(equivalenceLiteral, body);
138 } else if (literal instanceof CallLiteral callLiteral) {
139 translateCallLiteral(callLiteral, body);
140 } else if (literal instanceof ConstantLiteral constantLiteral) {
141 translateConstantLiteral(constantLiteral, body);
142 } else if (literal instanceof AssignLiteral<?> assignLiteral) {
143 translateAssignLiteral(assignLiteral, body);
144 } else if (literal instanceof AssumeLiteral assumeLiteral) {
145 translateAssumeLiteral(assumeLiteral, body);
146 } else if (literal instanceof CountLiteral countLiteral) {
147 translateCountLiteral(countLiteral, body);
148 } else if (literal instanceof AggregationLiteral<?, ?> aggregationLiteral) {
149 translateAggregationLiteral(aggregationLiteral, body);
150 } else {
151 throw new IllegalArgumentException("Unknown literal: " + literal.toString());
152 }
153 }
154
155 private void translateEquivalenceLiteral(EquivalenceLiteral equivalenceLiteral, PBody body) {
156 PVariable varSource = body.getOrCreateVariableByName(equivalenceLiteral.left().getUniqueName());
157 PVariable varTarget = body.getOrCreateVariableByName(equivalenceLiteral.right().getUniqueName());
158 if (equivalenceLiteral.positive()) {
159 new Equality(body, varSource, varTarget);
160 } else {
161 new Inequality(body, varSource, varTarget);
162 }
163 }
164
165 private void translateCallLiteral(CallLiteral callLiteral, PBody body) {
166 var polarity = callLiteral.getPolarity();
167 switch (polarity) {
168 case POSITIVE -> {
169 var substitution = translateSubstitution(callLiteral.getArguments(), body);
170 var constraint = callLiteral.getTarget();
171 if (constraint instanceof Dnf dnf) {
172 var pattern = translate(dnf);
173 new PositivePatternCall(body, substitution, pattern);
174 } else if (constraint instanceof AnySymbolView symbolView) {
175 var inputKey = wrapperFactory.getInputKey(symbolView);
176 new TypeConstraint(body, substitution, inputKey);
177 } else {
178 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
179 }
180 }
181 case TRANSITIVE -> {
182 var substitution = translateSubstitution(callLiteral.getArguments(), body);
183 var constraint = callLiteral.getTarget();
184 PQuery pattern;
185 if (constraint instanceof Dnf dnf) {
186 pattern = translate(dnf);
187 } else if (constraint instanceof AnySymbolView symbolView) {
188 pattern = wrapperFactory.wrapSymbolViewIdentityArguments(symbolView);
189 } else {
190 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
191 }
192 new BinaryTransitiveClosure(body, substitution, pattern);
193 }
194 case NEGATIVE -> {
195 var wrappedCall = wrapperFactory.maybeWrapConstraint(callLiteral);
196 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
197 var pattern = wrappedCall.pattern();
198 new NegativePatternCall(body, substitution, pattern);
199 }
200 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
201 }
202 }
203
204 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) {
205 int arity = substitution.size();
206 Object[] variables = new Object[arity];
207 for (int i = 0; i < arity; i++) {
208 var variable = substitution.get(i);
209 variables[i] = body.getOrCreateVariableByName(variable.getUniqueName());
210 }
211 return Tuples.flatTupleOf(variables);
212 }
213
214 private void translateConstantLiteral(ConstantLiteral constantLiteral, PBody body) {
215 var variable = body.getOrCreateVariableByName(constantLiteral.variable().getUniqueName());
216 new ConstantValue(body, variable, constantLiteral.nodeId());
217 }
218
219 private <T> void translateAssignLiteral(AssignLiteral<T> assignLiteral, PBody body) {
220 var variable = body.getOrCreateVariableByName(assignLiteral.variable().getUniqueName());
221 var term = assignLiteral.term();
222 if (term instanceof ConstantTerm<T> constantTerm) {
223 new ConstantValue(body, variable, constantTerm.getValue());
224 } else {
225 var evaluator = new TermEvaluator<>(term);
226 new ExpressionEvaluation(body, evaluator, variable);
227 }
228 }
229
230 private void translateAssumeLiteral(AssumeLiteral assumeLiteral, PBody body) {
231 var evaluator = new AssumptionEvaluator(assumeLiteral.term());
232 new ExpressionEvaluation(body, evaluator, null);
233 }
234
235 private void translateCountLiteral(CountLiteral countLiteral, PBody body) {
236 var wrappedCall = wrapperFactory.maybeWrapConstraint(countLiteral);
237 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
238 var resultVariable = body.getOrCreateVariableByName(countLiteral.getResultVariable().getUniqueName());
239 new PatternMatchCounter(body, substitution, wrappedCall.pattern(), resultVariable);
240 }
241
242 private <R, T> void translateAggregationLiteral(AggregationLiteral<R, T> aggregationLiteral, PBody body) {
243 var aggregator = aggregationLiteral.getAggregator();
244 IMultisetAggregationOperator<T, ?, R> aggregationOperator;
245 if (aggregator instanceof StatelessAggregator<R, T> statelessAggregator) {
246 aggregationOperator = new StatelessMultisetAggregator<>(statelessAggregator);
247 } else if (aggregator instanceof StatefulAggregator<R, T> statefulAggregator) {
248 aggregationOperator = new StatefulMultisetAggregator<>(statefulAggregator);
249 } else {
250 throw new IllegalArgumentException("Unknown aggregator: " + aggregator);
251 }
252 var wrappedCall = wrapperFactory.maybeWrapConstraint(aggregationLiteral);
253 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
254 var inputVariable = body.getOrCreateVariableByName(aggregationLiteral.getInputVariable().getUniqueName());
255 var aggregatedColumn = substitution.invertIndex().get(inputVariable);
256 if (aggregatedColumn == null) {
257 throw new IllegalStateException("Input variable %s not found in substitution %s".formatted(inputVariable,
258 substitution));
259 }
260 var boundAggregator = new BoundAggregator(aggregationOperator, aggregator.getInputType(),
261 aggregator.getResultType());
262 var resultVariable = body.getOrCreateVariableByName(aggregationLiteral.getResultVariable().getUniqueName());
263 new AggregatorConstraint(boundAggregator, body, substitution, wrappedCall.pattern(), resultVariable,
264 aggregatedColumn);
265 }
266}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
new file mode 100644
index 00000000..2b7280f2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
@@ -0,0 +1,189 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery;
7
8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
9import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
10import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
14import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
15import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
16import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
17import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
19import tools.refinery.store.query.Constraint;
20import tools.refinery.store.query.dnf.Dnf;
21import tools.refinery.store.query.dnf.DnfUtils;
22import tools.refinery.store.query.literal.AbstractCallLiteral;
23import tools.refinery.store.query.term.ParameterDirection;
24import tools.refinery.store.query.term.Variable;
25import tools.refinery.store.query.view.AnySymbolView;
26import tools.refinery.store.query.view.SymbolView;
27import tools.refinery.store.util.CycleDetectingMapper;
28
29import java.util.*;
30import java.util.function.ToIntFunction;
31
32class QueryWrapperFactory {
33 private final Dnf2PQuery dnf2PQuery;
34 private final Map<AnySymbolView, SymbolViewWrapper> view2WrapperMap = new LinkedHashMap<>();
35 private final CycleDetectingMapper<RemappedConstraint, RawPQuery> wrapConstraint = new CycleDetectingMapper<>(
36 RemappedConstraint::toString, this::doWrapConstraint);
37
38 QueryWrapperFactory(Dnf2PQuery dnf2PQuery) {
39 this.dnf2PQuery = dnf2PQuery;
40 }
41
42 public PQuery wrapSymbolViewIdentityArguments(AnySymbolView symbolView) {
43 var identity = new int[symbolView.arity()];
44 for (int i = 0; i < identity.length; i++) {
45 identity[i] = i;
46 }
47 return maybeWrapConstraint(symbolView, identity);
48 }
49
50 public WrappedCall maybeWrapConstraint(AbstractCallLiteral callLiteral) {
51 var arguments = callLiteral.getArguments();
52 int arity = arguments.size();
53 var remappedParameters = new int[arity];
54 var unboundVariableIndices = new HashMap<Variable, Integer>();
55 var appendVariable = new VariableAppender();
56 for (int i = 0; i < arity; i++) {
57 var variable = arguments.get(i);
58 // Unify all variables to avoid VIATRA bugs, even if they're bound in the containing clause.
59 remappedParameters[i] = unboundVariableIndices.computeIfAbsent(variable, appendVariable::applyAsInt);
60 }
61 var pattern = maybeWrapConstraint(callLiteral.getTarget(), remappedParameters);
62 return new WrappedCall(pattern, appendVariable.getRemappedArguments());
63 }
64
65 private PQuery maybeWrapConstraint(Constraint constraint, int[] remappedParameters) {
66 if (remappedParameters.length != constraint.arity()) {
67 throw new IllegalArgumentException("Constraint %s expected %d parameters, but got %d parameters".formatted(
68 constraint, constraint.arity(), remappedParameters.length));
69 }
70 if (constraint instanceof Dnf dnf && isIdentity(remappedParameters)) {
71 return dnf2PQuery.translate(dnf);
72 }
73 return wrapConstraint.map(new RemappedConstraint(constraint, remappedParameters));
74 }
75
76 private static boolean isIdentity(int[] remappedParameters) {
77 for (int i = 0; i < remappedParameters.length; i++) {
78 if (remappedParameters[i] != i) {
79 return false;
80 }
81 }
82 return true;
83 }
84
85 private RawPQuery doWrapConstraint(RemappedConstraint remappedConstraint) {
86 var constraint = remappedConstraint.constraint();
87 var remappedParameters = remappedConstraint.remappedParameters();
88
89 checkNoInputParameters(constraint);
90
91 var embeddedPQuery = new RawPQuery(DnfUtils.generateUniqueName(constraint.name()), PVisibility.EMBEDDED);
92 var body = new PBody(embeddedPQuery);
93 int arity = Arrays.stream(remappedParameters).max().orElse(-1) + 1;
94 var parameters = new ArrayList<PParameter>(arity);
95 var parameterVariables = new PVariable[arity];
96 var symbolicParameters = new ArrayList<ExportedParameter>(arity);
97 for (int i = 0; i < arity; i++) {
98 var parameterName = "p" + i;
99 var parameter = new PParameter(parameterName);
100 parameters.add(parameter);
101 var variable = body.getOrCreateVariableByName(parameterName);
102 parameterVariables[i] = variable;
103 symbolicParameters.add(new ExportedParameter(body, variable, parameter));
104 }
105 embeddedPQuery.setParameters(parameters);
106 body.setSymbolicParameters(symbolicParameters);
107
108 var arguments = new Object[remappedParameters.length];
109 for (int i = 0; i < remappedParameters.length; i++) {
110 arguments[i] = parameterVariables[remappedParameters[i]];
111 }
112 var argumentTuple = Tuples.flatTupleOf(arguments);
113
114 addPositiveConstraint(constraint, body, argumentTuple);
115 embeddedPQuery.addBody(body);
116 return embeddedPQuery;
117 }
118
119 private static void checkNoInputParameters(Constraint constraint) {
120 for (var constraintParameter : constraint.getParameters()) {
121 if (constraintParameter.getDirection() == ParameterDirection.IN) {
122 throw new IllegalArgumentException("Input parameter %s of %s is not supported"
123 .formatted(constraintParameter, constraint));
124 }
125 }
126 }
127
128 private void addPositiveConstraint(Constraint constraint, PBody body, Tuple argumentTuple) {
129 if (constraint instanceof SymbolView<?> view) {
130 new TypeConstraint(body, argumentTuple, getInputKey(view));
131 } else if (constraint instanceof Dnf dnf) {
132 var calledPQuery = dnf2PQuery.translate(dnf);
133 new PositivePatternCall(body, argumentTuple, calledPQuery);
134 } else {
135 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
136 }
137 }
138
139 public IInputKey getInputKey(AnySymbolView symbolView) {
140 return view2WrapperMap.computeIfAbsent(symbolView, SymbolViewWrapper::new);
141 }
142
143 public Map<AnySymbolView, IInputKey> getSymbolViews() {
144 return Collections.unmodifiableMap(view2WrapperMap);
145 }
146
147 public record WrappedCall(PQuery pattern, List<Variable> remappedArguments) {
148 }
149
150 private static class VariableAppender implements ToIntFunction<Variable> {
151 private final List<Variable> remappedArguments = new ArrayList<>();
152 private int nextIndex = 0;
153
154 @Override
155 public int applyAsInt(Variable variable) {
156 remappedArguments.add(variable);
157 int index = nextIndex;
158 nextIndex++;
159 return index;
160 }
161
162 public List<Variable> getRemappedArguments() {
163 return remappedArguments;
164 }
165 }
166
167 private record RemappedConstraint(Constraint constraint, int[] remappedParameters) {
168 @Override
169 public boolean equals(Object o) {
170 if (this == o) return true;
171 if (o == null || getClass() != o.getClass()) return false;
172 RemappedConstraint that = (RemappedConstraint) o;
173 return constraint.equals(that.constraint) && Arrays.equals(remappedParameters, that.remappedParameters);
174 }
175
176 @Override
177 public int hashCode() {
178 int result = Objects.hash(constraint);
179 result = 31 * result + Arrays.hashCode(remappedParameters);
180 return result;
181 }
182
183 @Override
184 public String toString() {
185 return "RemappedConstraint{constraint=%s, remappedParameters=%s}".formatted(constraint,
186 Arrays.toString(remappedParameters));
187 }
188 }
189}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
index 71b74396..255738c5 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
2 7
3import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification; 8import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
@@ -9,6 +14,7 @@ import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 14import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 15import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
11import tools.refinery.store.query.viatra.internal.RelationalScope; 16import tools.refinery.store.query.viatra.internal.RelationalScope;
17import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
12 18
13import java.util.LinkedHashSet; 19import java.util.LinkedHashSet;
14import java.util.List; 20import java.util.List;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
deleted file mode 100644
index e944e873..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
+++ /dev/null
@@ -1,72 +0,0 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
5import tools.refinery.store.query.ResultSet;
6import tools.refinery.store.query.viatra.ViatraTupleLike;
7import tools.refinery.store.tuple.Tuple;
8import tools.refinery.store.tuple.TupleLike;
9
10import java.util.Optional;
11import java.util.stream.Stream;
12
13public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet {
14 protected final Object[] empty;
15
16 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
17 super(specification);
18 empty = new Object[specification.getParameterNames().size()];
19 }
20
21 @Override
22 public boolean hasResult() {
23 return backend.hasMatch(empty);
24 }
25
26 @Override
27 public boolean hasResult(Tuple parameters) {
28 return backend.hasMatch(toParametersArray(parameters));
29 }
30
31 @Override
32 public Optional<TupleLike> oneResult() {
33 return backend.getOneArbitraryMatch(empty).map(ViatraTupleLike::new);
34 }
35
36 @Override
37 public Optional<TupleLike> oneResult(Tuple parameters) {
38 return backend.getOneArbitraryMatch(toParametersArray(parameters)).map(ViatraTupleLike::new);
39 }
40
41 @Override
42 public Stream<TupleLike> allResults() {
43 return backend.getAllMatches(empty).map(ViatraTupleLike::new);
44 }
45
46 @Override
47 public Stream<TupleLike> allResults(Tuple parameters) {
48 return backend.getAllMatches(toParametersArray(parameters)).map(ViatraTupleLike::new);
49 }
50
51 @Override
52 public int countResults() {
53 return backend.countMatches(empty);
54 }
55
56 @Override
57 public int countResults(Tuple parameters) {
58 return backend.countMatches(toParametersArray(parameters));
59 }
60
61 private Object[] toParametersArray(Tuple tuple) {
62 int size = tuple.getSize();
63 var array = new Object[tuple.getSize()];
64 for (int i = 0; i < size; i++) {
65 var value = tuple.get(i);
66 if (value >= 0) {
67 array[i] = Tuple.of(value);
68 }
69 }
70 return array;
71 }
72}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
new file mode 100644
index 00000000..461416f7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
@@ -0,0 +1,65 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery;
7
8import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
9import tools.refinery.store.query.term.StatefulAggregate;
10import tools.refinery.store.query.term.StatefulAggregator;
11
12import java.util.stream.Stream;
13
14record StatefulMultisetAggregator<R, T>(StatefulAggregator<R, T> aggregator)
15 implements IMultisetAggregationOperator<T, StatefulAggregate<R, T>, R> {
16 @Override
17 public String getShortDescription() {
18 return getName();
19 }
20
21 @Override
22 public String getName() {
23 return aggregator.toString();
24 }
25
26 @Override
27 public StatefulAggregate<R, T> createNeutral() {
28 return aggregator.createEmptyAggregate();
29 }
30
31 @Override
32 public boolean isNeutral(StatefulAggregate<R, T> result) {
33 return result.isEmpty();
34 }
35
36 @Override
37 public StatefulAggregate<R, T> update(StatefulAggregate<R, T> oldResult, T updateValue, boolean isInsertion) {
38 if (isInsertion) {
39 oldResult.add(updateValue);
40 } else {
41 oldResult.remove(updateValue);
42 }
43 return oldResult;
44 }
45
46 @Override
47 public R getAggregate(StatefulAggregate<R, T> result) {
48 return result.getResult();
49 }
50
51 @Override
52 public R aggregateStream(Stream<T> stream) {
53 return aggregator.aggregateStream(stream);
54 }
55
56 @Override
57 public StatefulAggregate<R, T> clone(StatefulAggregate<R, T> original) {
58 return original.deepCopy();
59 }
60
61 @Override
62 public boolean contains(T value, StatefulAggregate<R, T> accumulator) {
63 return accumulator.contains(value);
64 }
65}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
new file mode 100644
index 00000000..49175d75
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
@@ -0,0 +1,55 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery;
7
8import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
9import tools.refinery.store.query.term.StatelessAggregator;
10
11import java.util.stream.Stream;
12
13record StatelessMultisetAggregator<R, T>(StatelessAggregator<R, T> aggregator)
14 implements IMultisetAggregationOperator<T, R, R> {
15 @Override
16 public String getShortDescription() {
17 return getName();
18 }
19
20 @Override
21 public String getName() {
22 return aggregator.toString();
23 }
24
25 @Override
26 public R createNeutral() {
27 return aggregator.getEmptyResult();
28 }
29
30 @Override
31 public boolean isNeutral(R result) {
32 return createNeutral().equals(result);
33 }
34
35 @Override
36 public R update(R oldResult, T updateValue, boolean isInsertion) {
37 return isInsertion ? aggregator.add(oldResult, updateValue) : aggregator.remove(oldResult, updateValue);
38 }
39
40 @Override
41 public R getAggregate(R result) {
42 return result;
43 }
44
45 @Override
46 public R clone(R original) {
47 // Aggregate result is immutable.
48 return original;
49 }
50
51 @Override
52 public R aggregateStream(Stream<T> stream) {
53 return aggregator.aggregateStream(stream);
54 }
55}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java
index c442add8..a777613e 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/SymbolViewWrapper.java
@@ -1,10 +1,15 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.pquery; 6package tools.refinery.store.query.viatra.internal.pquery;
2 7
3import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper; 8import org.eclipse.viatra.query.runtime.matchers.context.common.BaseInputKeyWrapper;
4import tools.refinery.store.query.view.AnyRelationView; 9import tools.refinery.store.query.view.AnySymbolView;
5 10
6public class RelationViewWrapper extends BaseInputKeyWrapper<AnyRelationView> { 11public class SymbolViewWrapper extends BaseInputKeyWrapper<AnySymbolView> {
7 public RelationViewWrapper(AnyRelationView wrappedKey) { 12 public SymbolViewWrapper(AnySymbolView wrappedKey) {
8 super(wrappedKey); 13 super(wrappedKey);
9 } 14 }
10 15
@@ -27,4 +32,9 @@ public class RelationViewWrapper extends BaseInputKeyWrapper<AnyRelationView> {
27 public boolean isEnumerable() { 32 public boolean isEnumerable() {
28 return true; 33 return true;
29 } 34 }
35
36 @Override
37 public String toString() {
38 return "RelationViewWrapper{wrappedKey=%s}".formatted(wrappedKey);
39 }
30} 40}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
new file mode 100644
index 00000000..1187f57a
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
@@ -0,0 +1,37 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery;
7
8import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator;
9import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
10import tools.refinery.store.query.term.Term;
11import tools.refinery.store.query.term.Variable;
12
13import java.util.stream.Collectors;
14
15class TermEvaluator<T> implements IExpressionEvaluator {
16 private final Term<T> term;
17
18 public TermEvaluator(Term<T> term) {
19 this.term = term;
20 }
21
22 @Override
23 public String getShortDescription() {
24 return term.toString();
25 }
26
27 @Override
28 public Iterable<String> getInputParameterNames() {
29 return term.getInputVariables().stream().map(Variable::getUniqueName).collect(Collectors.toUnmodifiableSet());
30 }
31
32 @Override
33 public Object evaluateExpression(IValueProvider provider) {
34 var valuation = new ValueProviderBasedValuation(provider);
35 return term.evaluate(valuation);
36 }
37}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
new file mode 100644
index 00000000..62cb8b3a
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
@@ -0,0 +1,19 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.pquery;
7
8import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
9import tools.refinery.store.query.term.DataVariable;
10import tools.refinery.store.query.valuation.Valuation;
11
12public record ValueProviderBasedValuation(IValueProvider valueProvider) implements Valuation {
13 @Override
14 public <T> T getValue(DataVariable<T> variable) {
15 @SuppressWarnings("unchecked")
16 var value = (T) valueProvider.getValue(variable.getUniqueName());
17 return value;
18 }
19}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
index 8a467066..986bb0b1 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
@@ -1,47 +1,51 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
2 7
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; 9import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 10import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 11import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
7import tools.refinery.store.query.view.AnyRelationView; 12import tools.refinery.store.query.view.AnySymbolView;
8import tools.refinery.store.query.view.RelationView; 13import tools.refinery.store.query.view.SymbolView;
9 14
10import java.util.HashMap; 15import java.util.HashMap;
11import java.util.Map; 16import java.util.Map;
12 17
13public class ModelUpdateListener { 18public class ModelUpdateListener {
14 private final Map<AnyRelationView, RelationViewUpdateListener<?>> relationViewUpdateListeners; 19 private final Map<AnySymbolView, SymbolViewUpdateListener<?>> symbolViewUpdateListeners;
15 20
16 public ModelUpdateListener(ViatraModelQueryAdapterImpl adapter) { 21 public ModelUpdateListener(ViatraModelQueryAdapterImpl adapter) {
17 var relationViews = adapter.getStoreAdapter().getInputKeys().keySet(); 22 var symbolViews = adapter.getStoreAdapter().getInputKeys().keySet();
18 relationViewUpdateListeners = new HashMap<>(relationViews.size()); 23 symbolViewUpdateListeners = new HashMap<>(symbolViews.size());
19 for (var relationView : relationViews) { 24 for (var symbolView : symbolViews) {
20 registerView(adapter, (RelationView<?>) relationView); 25 registerView(adapter, (SymbolView<?>) symbolView);
21 } 26 }
22 } 27 }
23 28
24 private <T> void registerView(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) { 29 private <T> void registerView(ViatraModelQueryAdapterImpl adapter, SymbolView<T> view) {
25 var listener = RelationViewUpdateListener.of(adapter, relationView);
26 var model = adapter.getModel(); 30 var model = adapter.getModel();
27 var interpretation = model.getInterpretation(relationView.getSymbol()); 31 var interpretation = model.getInterpretation(view.getSymbol());
28 interpretation.addListener(listener, true); 32 var listener = SymbolViewUpdateListener.of(adapter, view, interpretation);
29 relationViewUpdateListeners.put(relationView, listener); 33 symbolViewUpdateListeners.put(view, listener);
30 } 34 }
31 35
32 public boolean containsRelationView(AnyRelationView relationView) { 36 public boolean containsSymbolView(AnySymbolView relationView) {
33 return relationViewUpdateListeners.containsKey(relationView); 37 return symbolViewUpdateListeners.containsKey(relationView);
34 } 38 }
35 39
36 public void addListener(IInputKey key, AnyRelationView relationView, ITuple seed, 40 public void addListener(IInputKey key, AnySymbolView symbolView, ITuple seed,
37 IQueryRuntimeContextListener listener) { 41 IQueryRuntimeContextListener listener) {
38 var relationViewUpdateListener = relationViewUpdateListeners.get(relationView); 42 var symbolViewUpdateListener = symbolViewUpdateListeners.get(symbolView);
39 relationViewUpdateListener.addFilter(key, seed, listener); 43 symbolViewUpdateListener.addFilter(key, seed, listener);
40 } 44 }
41 45
42 public void removeListener(IInputKey key, AnyRelationView relationView, ITuple seed, 46 public void removeListener(IInputKey key, AnySymbolView symbolView, ITuple seed,
43 IQueryRuntimeContextListener listener) { 47 IQueryRuntimeContextListener listener) {
44 var relationViewUpdateListener = relationViewUpdateListeners.get(relationView); 48 var symbolViewUpdateListener = symbolViewUpdateListeners.get(symbolView);
45 relationViewUpdateListener.removeFilter(key, seed, listener); 49 symbolViewUpdateListener.removeFilter(key, seed, listener);
46 } 50 }
47} 51}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
index 221f1b4a..efdbfcbe 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewFilter.java
@@ -1,3 +1,8 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
2 7
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java
deleted file mode 100644
index bf6b4197..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java
+++ /dev/null
@@ -1,48 +0,0 @@
1package tools.refinery.store.query.viatra.internal.update;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
7import tools.refinery.store.model.InterpretationListener;
8import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
9import tools.refinery.store.query.view.RelationView;
10import tools.refinery.store.query.view.TuplePreservingRelationView;
11
12import java.util.ArrayList;
13import java.util.List;
14
15public abstract class RelationViewUpdateListener<T> implements InterpretationListener<T> {
16 private final ViatraModelQueryAdapterImpl adapter;
17 private final List<RelationViewFilter> filters = new ArrayList<>();
18
19 protected RelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter) {
20 this.adapter = adapter;
21 }
22
23 public void addFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
24 filters.add(new RelationViewFilter(inputKey, seed, listener));
25 }
26
27 public void removeFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
28 filters.remove(new RelationViewFilter(inputKey, seed, listener));
29 }
30
31 protected void processUpdate(Tuple tuple, boolean isInsertion) {
32 adapter.markAsPending();
33 int size = filters.size();
34 // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead.
35 //noinspection ForLoopReplaceableByForEach
36 for (int i = 0; i < size; i++) {
37 filters.get(i).update(tuple, isInsertion);
38 }
39 }
40
41 public static <T> RelationViewUpdateListener<T> of(ViatraModelQueryAdapterImpl adapter,
42 RelationView<T> relationView) {
43 if (relationView instanceof TuplePreservingRelationView<T> tuplePreservingRelationView) {
44 return new TuplePreservingRelationViewUpdateListener<>(adapter, tuplePreservingRelationView);
45 }
46 return new TupleChangingRelationViewUpdateListener<>(adapter, relationView);
47 }
48}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java
new file mode 100644
index 00000000..f1a2ac7c
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/SymbolViewUpdateListener.java
@@ -0,0 +1,65 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.update;
7
8import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
9import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
10import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
11import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
12import tools.refinery.store.model.Interpretation;
13import tools.refinery.store.model.InterpretationListener;
14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
15import tools.refinery.store.query.view.SymbolView;
16import tools.refinery.store.query.view.TuplePreservingView;
17
18import java.util.ArrayList;
19import java.util.List;
20
21public abstract class SymbolViewUpdateListener<T> implements InterpretationListener<T> {
22 private final ViatraModelQueryAdapterImpl adapter;
23 private final Interpretation<T> interpretation;
24 private final List<RelationViewFilter> filters = new ArrayList<>();
25
26 protected SymbolViewUpdateListener(ViatraModelQueryAdapterImpl adapter, Interpretation<T> interpretation) {
27 this.adapter = adapter;
28 this.interpretation = interpretation;
29 }
30
31 public void addFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
32 if (filters.isEmpty()) {
33 // First filter to be added, from now on we have to subscribe to model updates.
34 interpretation.addListener(this, true);
35 }
36 filters.add(new RelationViewFilter(inputKey, seed, listener));
37 }
38
39 public void removeFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
40 if (filters.remove(new RelationViewFilter(inputKey, seed, listener)) && filters.isEmpty()) {
41 // Last listener to be added, we don't have be subscribed to model updates anymore.
42 interpretation.removeListener(this);
43 }
44 }
45
46 protected void processUpdate(Tuple tuple, boolean isInsertion) {
47 adapter.markAsPending();
48 int size = filters.size();
49 // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead.
50 //noinspection ForLoopReplaceableByForEach
51 for (int i = 0; i < size; i++) {
52 filters.get(i).update(tuple, isInsertion);
53 }
54 }
55
56 public static <T> SymbolViewUpdateListener<T> of(ViatraModelQueryAdapterImpl adapter,
57 SymbolView<T> view,
58 Interpretation<T> interpretation) {
59 if (view instanceof TuplePreservingView<T> tuplePreservingRelationView) {
60 return new TuplePreservingViewUpdateListener<>(adapter, tuplePreservingRelationView,
61 interpretation);
62 }
63 return new TupleChangingViewUpdateListener<>(adapter, view, interpretation);
64 }
65}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java
deleted file mode 100644
index 14142884..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java
+++ /dev/null
@@ -1,37 +0,0 @@
1package tools.refinery.store.query.viatra.internal.update;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
4import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
5import tools.refinery.store.query.view.RelationView;
6import tools.refinery.store.tuple.Tuple;
7
8import java.util.Arrays;
9
10public class TupleChangingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> {
11 private final RelationView<T> relationView;
12
13 TupleChangingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) {
14 super(adapter);
15 this.relationView = relationView;
16 }
17
18 @Override
19 public void put(Tuple key, T fromValue, T toValue, boolean restoring) {
20 boolean fromPresent = relationView.filter(key, fromValue);
21 boolean toPresent = relationView.filter(key, toValue);
22 if (fromPresent) {
23 if (toPresent) { // value change
24 var fromArray = relationView.forwardMap(key, fromValue);
25 var toArray = relationView.forwardMap(key, toValue);
26 if (!Arrays.equals(fromArray, toArray)) {
27 processUpdate(Tuples.flatTupleOf(fromArray), false);
28 processUpdate(Tuples.flatTupleOf(toArray), true);
29 }
30 } else { // fromValue disappears
31 processUpdate(Tuples.flatTupleOf(relationView.forwardMap(key, fromValue)), false);
32 }
33 } else if (toPresent) { // toValue appears
34 processUpdate(Tuples.flatTupleOf(relationView.forwardMap(key, toValue)), true);
35 }
36 }
37}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java
new file mode 100644
index 00000000..45d35571
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingViewUpdateListener.java
@@ -0,0 +1,44 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.query.viatra.internal.update;
7
8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
9import tools.refinery.store.model.Interpretation;
10import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
11import tools.refinery.store.query.view.SymbolView;
12import tools.refinery.store.tuple.Tuple;
13
14import java.util.Arrays;
15
16public class TupleChangingViewUpdateListener<T> extends SymbolViewUpdateListener<T> {
17 private final SymbolView<T> view;
18
19 TupleChangingViewUpdateListener(ViatraModelQueryAdapterImpl adapter, SymbolView<T> view,
20 Interpretation<T> interpretation) {
21 super(adapter, interpretation);
22 this.view = view;
23 }
24
25 @Override
26 public void put(Tuple key, T fromValue, T toValue, boolean restoring) {
27 boolean fromPresent = view.filter(key, fromValue);
28 boolean toPresent = view.filter(key, toValue);
29 if (fromPresent) {
30 if (toPresent) { // value change
31 var fromArray = view.forwardMap(key, fromValue);
32 var toArray = view.forwardMap(key, toValue);
33 if (!Arrays.equals(fromArray, toArray)) {
34 processUpdate(Tuples.flatTupleOf(fromArray), false);
35 processUpdate(Tuples.flatTupleOf(toArray), true);
36 }
37 } else { // fromValue disappears
38 processUpdate(Tuples.flatTupleOf(view.forwardMap(key, fromValue)), false);
39 }
40 } else if (toPresent) { // toValue appears
41 processUpdate(Tuples.flatTupleOf(view.forwardMap(key, toValue)), true);
42 }
43 }
44}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java
index 288e018a..c18dbafb 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingViewUpdateListener.java
@@ -1,16 +1,22 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
1package tools.refinery.store.query.viatra.internal.update; 6package tools.refinery.store.query.viatra.internal.update;
2 7
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 8import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
9import tools.refinery.store.model.Interpretation;
4import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 10import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
5import tools.refinery.store.query.view.TuplePreservingRelationView; 11import tools.refinery.store.query.view.TuplePreservingView;
6import tools.refinery.store.tuple.Tuple; 12import tools.refinery.store.tuple.Tuple;
7 13
8public class TuplePreservingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> { 14public class TuplePreservingViewUpdateListener<T> extends SymbolViewUpdateListener<T> {
9 private final TuplePreservingRelationView<T> view; 15 private final TuplePreservingView<T> view;
10 16
11 TuplePreservingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, 17 TuplePreservingViewUpdateListener(ViatraModelQueryAdapterImpl adapter, TuplePreservingView<T> view,
12 TuplePreservingRelationView<T> view) { 18 Interpretation<T> interpretation) {
13 super(adapter); 19 super(adapter, interpretation);
14 this.view = view; 20 this.view = view;
15 } 21 }
16 22