aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java/tools
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-07 16:26:26 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-31 15:38:59 +0200
commit372058e54825ab58a66c25ae528e81a656c22659 (patch)
tree3686057057ebcad2faae7233dc691ecacc3e9fe2 /subprojects/store-query-viatra/src/main/java/tools
parentrefactor: use Cursor in query result sets (diff)
downloadrefinery-372058e54825ab58a66c25ae528e81a656c22659.tar.gz
refinery-372058e54825ab58a66c25ae528e81a656c22659.tar.zst
refinery-372058e54825ab58a66c25ae528e81a656c22659.zip
feat: terms and improved query evaluation
* Implement data terms for computations in queries. * Function-like queries with computed results. * Improved query evaluation, including positive and negative diagonal cosntraints. * Preliminary local search support. * Changes to the DNF representation for count and aggregation support. feat: terms wip feat: query terms wip feat: query evaluation, diagonal constraints, local search wip fix reasoning compilation wip
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/ViatraModelQueryBuilder.java11
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java51
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java63
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java19
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java31
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java75
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java116
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java30
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java136
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java55
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java23
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java65
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java48
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java91
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java)2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java50
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java23
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java15
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ResultSetCursor.java)7
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java89
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java52
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java)9
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java201
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java173
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java1
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java93
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java60
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java50
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java32
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java14
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java3
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java5
37 files changed, 1514 insertions, 250 deletions
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 24aa52e2..7ae86f9f 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
@@ -4,7 +4,8 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
4import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 4import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
5import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 5import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
6import tools.refinery.store.model.ModelStore; 6import tools.refinery.store.model.ModelStore;
7import tools.refinery.store.query.Dnf; 7import tools.refinery.store.query.dnf.AnyQuery;
8import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.ModelQueryBuilder; 9import tools.refinery.store.query.ModelQueryBuilder;
9 10
10import java.util.Collection; 11import java.util.Collection;
@@ -23,21 +24,21 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder {
23 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); 24 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory);
24 25
25 @Override 26 @Override
26 default ViatraModelQueryBuilder queries(Dnf... queries) { 27 default ViatraModelQueryBuilder queries(AnyQuery... queries) {
27 ModelQueryBuilder.super.queries(queries); 28 ModelQueryBuilder.super.queries(queries);
28 return this; 29 return this;
29 } 30 }
30 31
31 @Override 32 @Override
32 default ViatraModelQueryBuilder queries(Collection<Dnf> queries) { 33 default ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) {
33 ModelQueryBuilder.super.queries(queries); 34 ModelQueryBuilder.super.queries(queries);
34 return this; 35 return this;
35 } 36 }
36 37
37 @Override 38 @Override
38 ViatraModelQueryBuilder query(Dnf query); 39 ViatraModelQueryBuilder query(AnyQuery query);
39 40
40 ViatraModelQueryBuilder query(Dnf query, QueryEvaluationHint queryEvaluationHint); 41 ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint);
41 42
42 ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint); 43 ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint);
43 44
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 37700413..8be30fee 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
@@ -7,10 +7,18 @@ import org.eclipse.viatra.query.runtime.internal.apiimpl.ViatraQueryEngineImpl;
7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend; 7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
9import tools.refinery.store.model.Model; 9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.Dnf; 10import tools.refinery.store.model.ModelListener;
11import tools.refinery.store.query.AnyResultSet;
11import tools.refinery.store.query.EmptyResultSet; 12import tools.refinery.store.query.EmptyResultSet;
12import tools.refinery.store.query.ResultSet; 13import tools.refinery.store.query.ResultSet;
14import tools.refinery.store.query.dnf.AnyQuery;
15import tools.refinery.store.query.dnf.FunctionalQuery;
16import tools.refinery.store.query.dnf.Query;
17import tools.refinery.store.query.dnf.RelationalQuery;
13import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; 18import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
19import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher;
20import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
21import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher;
14 22
15import java.lang.invoke.MethodHandle; 23import java.lang.invoke.MethodHandle;
16import java.lang.invoke.MethodHandles; 24import java.lang.invoke.MethodHandles;
@@ -19,7 +27,7 @@ import java.util.Collections;
19import java.util.LinkedHashMap; 27import java.util.LinkedHashMap;
20import java.util.Map; 28import java.util.Map;
21 29
22public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { 30public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, ModelListener {
23 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery"; 31 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery";
24 private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE; 32 private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE;
25 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends"; 33 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends";
@@ -28,8 +36,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
28 private final Model model; 36 private final Model model;
29 private final ViatraModelQueryStoreAdapterImpl storeAdapter; 37 private final ViatraModelQueryStoreAdapterImpl storeAdapter;
30 private final ViatraQueryEngineImpl queryEngine; 38 private final ViatraQueryEngineImpl queryEngine;
31 39 private final Map<AnyQuery, AnyResultSet> resultSets;
32 private final Map<Dnf, ResultSet> resultSets;
33 private boolean pendingChanges; 40 private boolean pendingChanges;
34 41
35 static { 42 static {
@@ -49,9 +56,8 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
49 this.model = model; 56 this.model = model;
50 this.storeAdapter = storeAdapter; 57 this.storeAdapter = storeAdapter;
51 var scope = new RelationalScope(this); 58 var scope = new RelationalScope(this);
52 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope); 59 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope,
53 60 storeAdapter.getEngineOptions());
54
55 61
56 var querySpecifications = storeAdapter.getQuerySpecifications(); 62 var querySpecifications = storeAdapter.getQuerySpecifications();
57 GenericQueryGroup.of( 63 GenericQueryGroup.of(
@@ -60,14 +66,28 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
60 var vacuousQueries = storeAdapter.getVacuousQueries(); 66 var vacuousQueries = storeAdapter.getVacuousQueries();
61 resultSets = new LinkedHashMap<>(querySpecifications.size() + vacuousQueries.size()); 67 resultSets = new LinkedHashMap<>(querySpecifications.size() + vacuousQueries.size());
62 for (var entry : querySpecifications.entrySet()) { 68 for (var entry : querySpecifications.entrySet()) {
63 var matcher = queryEngine.getMatcher(entry.getValue()); 69 var rawPatternMatcher = queryEngine.getMatcher(entry.getValue());
64 resultSets.put(entry.getKey(), matcher); 70 var query = entry.getKey();
71 resultSets.put(query, createResultSet((Query<?>) query, rawPatternMatcher));
65 } 72 }
66 for (var vacuousQuery : vacuousQueries) { 73 for (var vacuousQuery : vacuousQueries) {
67 resultSets.put(vacuousQuery, new EmptyResultSet()); 74 resultSets.put(vacuousQuery, new EmptyResultSet<>(this, (Query<?>) vacuousQuery));
68 } 75 }
69 76
70 setUpdatePropagationDelayed(true); 77 setUpdatePropagationDelayed(true);
78 model.addListener(this);
79 }
80
81 private <T> ResultSet<T> createResultSet(Query<T> query, RawPatternMatcher matcher) {
82 if (query instanceof RelationalQuery relationalQuery) {
83 @SuppressWarnings("unchecked")
84 var resultSet = (ResultSet<T>) new RelationalViatraMatcher(this, relationalQuery, matcher);
85 return resultSet;
86 } else if (query instanceof FunctionalQuery<T> functionalQuery) {
87 return new FunctionalViatraMatcher<>(this, functionalQuery, matcher);
88 } else {
89 throw new IllegalArgumentException("Unknown query: " + query);
90 }
71 } 91 }
72 92
73 private void setUpdatePropagationDelayed(boolean value) { 93 private void setUpdatePropagationDelayed(boolean value) {
@@ -105,12 +125,14 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
105 } 125 }
106 126
107 @Override 127 @Override
108 public ResultSet getResultSet(Dnf query) { 128 public <T> ResultSet<T> getResultSet(Query<T> query) {
109 var resultSet = resultSets.get(query); 129 var resultSet = resultSets.get(query);
110 if (resultSet == null) { 130 if (resultSet == null) {
111 throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name())); 131 throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name()));
112 } 132 }
113 return resultSet; 133 @SuppressWarnings("unchecked")
134 var typedResultSet = (ResultSet<T>) resultSet;
135 return typedResultSet;
114 } 136 }
115 137
116 @Override 138 @Override
@@ -142,4 +164,9 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
142 } 164 }
143 pendingChanges = false; 165 pendingChanges = false;
144 } 166 }
167
168 @Override
169 public void afterRestore() {
170 flushChanges();
171 }
145} 172}
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 13641ace..bfabf26e 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
@@ -2,33 +2,40 @@ package tools.refinery.store.query.viatra.internal;
2 2
3import org.eclipse.viatra.query.runtime.api.IQuerySpecification; 3import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; 5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHintOptions;
6import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 6import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
7import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 7import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
8import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory; 8import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory;
9import tools.refinery.store.adapter.AbstractModelAdapterBuilder; 9import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
10import tools.refinery.store.model.ModelStore; 10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.model.ModelStoreBuilder; 11import tools.refinery.store.model.ModelStoreBuilder;
12import tools.refinery.store.query.Dnf; 12import tools.refinery.store.query.dnf.AnyQuery;
13import tools.refinery.store.query.dnf.Dnf;
13import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; 14import tools.refinery.store.query.viatra.ViatraModelQueryBuilder;
15import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction;
16import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory;
17import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
14import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; 18import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery;
15import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher;
16 19
17import java.util.*; 20import java.util.*;
18import java.util.function.Function; 21import java.util.function.Function;
19 22
20public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder { 23public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder {
21 private ViatraQueryEngineOptions.Builder engineOptionsBuilder; 24 private ViatraQueryEngineOptions.Builder engineOptionsBuilder;
25 private QueryEvaluationHint defaultHint = new QueryEvaluationHint(Map.of(
26 // Use a cost function that ignores the initial (empty) model but allows higher arity input keys.
27 LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction()
28 ), (IQueryBackendFactory) null);
22 private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); 29 private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery();
23 private final Set<Dnf> vacuousQueries = new LinkedHashSet<>(); 30 private final Set<AnyQuery> vacuousQueries = new LinkedHashSet<>();
24 private final Map<Dnf, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>(); 31 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>();
25 32
26 public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) { 33 public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) {
27 super(storeBuilder); 34 super(storeBuilder);
28 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder() 35 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder()
29 .withDefaultBackend(ReteBackendFactory.INSTANCE) 36 .withDefaultBackend(ReteBackendFactory.INSTANCE)
30 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE) 37 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE)
31 .withDefaultSearchBackend(LocalSearchGenericBackendFactory.INSTANCE); 38 .withDefaultSearchBackend(RelationalLocalSearchBackendFactory.INSTANCE);
32 } 39 }
33 40
34 @Override 41 @Override
@@ -39,7 +46,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
39 46
40 @Override 47 @Override
41 public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) { 48 public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) {
42 engineOptionsBuilder.withDefaultHint(queryEvaluationHint); 49 defaultHint = defaultHint.overrideBy(queryEvaluationHint);
43 return this; 50 return this;
44 } 51 }
45 52
@@ -62,15 +69,16 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
62 } 69 }
63 70
64 @Override 71 @Override
65 public ViatraModelQueryBuilder query(Dnf query) { 72 public ViatraModelQueryBuilder query(AnyQuery query) {
66 if (querySpecifications.containsKey(query) || vacuousQueries.contains(query)) { 73 if (querySpecifications.containsKey(query) || vacuousQueries.contains(query)) {
67 // Ignore duplicate queries. 74 // Ignore duplicate queries.
68 return this; 75 return this;
69 } 76 }
70 var reduction = query.getReduction(); 77 var dnf = query.getDnf();
78 var reduction = dnf.getReduction();
71 switch (reduction) { 79 switch (reduction) {
72 case NOT_REDUCIBLE -> { 80 case NOT_REDUCIBLE -> {
73 var pQuery = dnf2PQuery.translate(query); 81 var pQuery = dnf2PQuery.translate(dnf);
74 querySpecifications.put(query, pQuery.build()); 82 querySpecifications.put(query, pQuery.build());
75 } 83 }
76 case ALWAYS_FALSE -> vacuousQueries.add(query); 84 case ALWAYS_FALSE -> vacuousQueries.add(query);
@@ -82,9 +90,9 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
82 } 90 }
83 91
84 @Override 92 @Override
85 public ViatraModelQueryBuilder query(Dnf query, QueryEvaluationHint queryEvaluationHint) { 93 public ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint) {
94 hint(query.getDnf(), queryEvaluationHint);
86 query(query); 95 query(query);
87 hint(query, queryEvaluationHint);
88 return this; 96 return this;
89 } 97 }
90 98
@@ -96,26 +104,35 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
96 104
97 @Override 105 @Override
98 public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) { 106 public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) {
99 var pQuery = dnf2PQuery.getAlreadyTranslated(dnf); 107 dnf2PQuery.hint(dnf, queryEvaluationHint);
100 if (pQuery == null) {
101 if (vacuousQueries.contains(dnf)) {
102 // Ignore hits for queries that will never be executed by the query engine.
103 return this;
104 }
105 throw new IllegalArgumentException(
106 "Cannot specify hint for %s, because it was not added to the query engine".formatted(dnf.name()));
107 }
108 pQuery.setEvaluationHints(pQuery.getEvaluationHints().overrideBy(queryEvaluationHint));
109 return this; 108 return this;
110 } 109 }
111 110
112 @Override 111 @Override
113 public ViatraModelQueryStoreAdapterImpl createStoreAdapter(ModelStore store) { 112 public ViatraModelQueryStoreAdapterImpl createStoreAdapter(ModelStore store) {
114 validateSymbols(store); 113 validateSymbols(store);
115 return new ViatraModelQueryStoreAdapterImpl(store, engineOptionsBuilder.build(), dnf2PQuery.getRelationViews(), 114 dnf2PQuery.assertNoUnusedHints();
115 return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getRelationViews(),
116 Collections.unmodifiableMap(querySpecifications), Collections.unmodifiableSet(vacuousQueries)); 116 Collections.unmodifiableMap(querySpecifications), Collections.unmodifiableSet(vacuousQueries));
117 } 117 }
118 118
119 private ViatraQueryEngineOptions buildEngineOptions() {
120 // Workaround: manually override the default backend, because {@link ViatraQueryEngineOptions.Builder}
121 // ignores all backend requirements except {@code SPECIFIC}.
122 switch (defaultHint.getQueryBackendRequirementType()) {
123 case SPECIFIC -> engineOptionsBuilder.withDefaultBackend(defaultHint.getQueryBackendFactory());
124 case DEFAULT_CACHING -> engineOptionsBuilder.withDefaultBackend(
125 engineOptionsBuilder.build().getDefaultCachingBackendFactory());
126 case DEFAULT_SEARCH -> engineOptionsBuilder.withDefaultBackend(
127 engineOptionsBuilder.build().getDefaultSearchBackendFactory());
128 case UNSPECIFIED -> {
129 // Nothing to do, leave the default backend unchanged.
130 }
131 }
132 engineOptionsBuilder.withDefaultHint(defaultHint);
133 return engineOptionsBuilder.build();
134 }
135
119 private void validateSymbols(ModelStore store) { 136 private void validateSymbols(ModelStore store) {
120 var symbols = store.getSymbols(); 137 var symbols = store.getSymbols();
121 for (var relationView : dnf2PQuery.getRelationViews().keySet()) { 138 for (var relationView : dnf2PQuery.getRelationViews().keySet()) {
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 00660d0b..04c48c43 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
@@ -5,9 +5,9 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
6import tools.refinery.store.model.Model; 6import tools.refinery.store.model.Model;
7import tools.refinery.store.model.ModelStore; 7import tools.refinery.store.model.ModelStore;
8import tools.refinery.store.query.Dnf; 8import tools.refinery.store.query.dnf.AnyQuery;
9import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter; 9import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter;
10import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher; 10import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
11import tools.refinery.store.query.view.AnyRelationView; 11import tools.refinery.store.query.view.AnyRelationView;
12 12
13import java.util.*; 13import java.util.*;
@@ -16,20 +16,20 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd
16 private final ModelStore store; 16 private final ModelStore store;
17 private final ViatraQueryEngineOptions engineOptions; 17 private final ViatraQueryEngineOptions engineOptions;
18 private final Map<AnyRelationView, IInputKey> inputKeys; 18 private final Map<AnyRelationView, IInputKey> inputKeys;
19 private final Map<Dnf, IQuerySpecification<RawPatternMatcher>> querySpecifications; 19 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications;
20 private final Set<Dnf> vacuousQueries; 20 private final Set<AnyQuery> vacuousQueries;
21 private final Set<Dnf> allQueries; 21 private final Set<AnyQuery> allQueries;
22 22
23 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, 23 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions,
24 Map<AnyRelationView, IInputKey> inputKeys, 24 Map<AnyRelationView, IInputKey> inputKeys,
25 Map<Dnf, IQuerySpecification<RawPatternMatcher>> querySpecifications, 25 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications,
26 Set<Dnf> vacuousQueries) { 26 Set<AnyQuery> vacuousQueries) {
27 this.store = store; 27 this.store = store;
28 this.engineOptions = engineOptions; 28 this.engineOptions = engineOptions;
29 this.inputKeys = inputKeys; 29 this.inputKeys = inputKeys;
30 this.querySpecifications = querySpecifications; 30 this.querySpecifications = querySpecifications;
31 this.vacuousQueries = vacuousQueries; 31 this.vacuousQueries = vacuousQueries;
32 var mutableAllQueries = new LinkedHashSet<Dnf>(querySpecifications.size() + vacuousQueries.size()); 32 var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size());
33 mutableAllQueries.addAll(querySpecifications.keySet()); 33 mutableAllQueries.addAll(querySpecifications.keySet());
34 mutableAllQueries.addAll(vacuousQueries); 34 mutableAllQueries.addAll(vacuousQueries);
35 this.allQueries = Collections.unmodifiableSet(mutableAllQueries); 35 this.allQueries = Collections.unmodifiableSet(mutableAllQueries);
@@ -49,15 +49,15 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd
49 } 49 }
50 50
51 @Override 51 @Override
52 public Collection<Dnf> getQueries() { 52 public Collection<AnyQuery> getQueries() {
53 return allQueries; 53 return allQueries;
54 } 54 }
55 55
56 Map<Dnf, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() { 56 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() {
57 return querySpecifications; 57 return querySpecifications;
58 } 58 }
59 59
60 Set<Dnf> getVacuousQueries() { 60 Set<AnyQuery> getVacuousQueries() {
61 return vacuousQueries; 61 return vacuousQueries;
62 } 62 }
63 63
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..d2c6beb4 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
@@ -3,6 +3,8 @@ package tools.refinery.store.query.viatra.internal.context;
3import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext; 3import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext;
4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
5import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; 5import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication;
6import org.eclipse.viatra.query.runtime.matchers.context.common.JavaTransitiveInstancesKey;
7import tools.refinery.store.query.term.DataSort;
6import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper; 8import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper;
7import tools.refinery.store.query.view.AnyRelationView; 9import tools.refinery.store.query.view.AnyRelationView;
8 10
@@ -37,6 +39,9 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
37 39
38 @Override 40 @Override
39 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) { 41 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) {
42 if (implyingKey instanceof JavaTransitiveInstancesKey) {
43 return List.of();
44 }
40 var relationView = checkKey(implyingKey); 45 var relationView = checkKey(implyingKey);
41 var relationViewImplications = relationView.getImpliedRelationViews(); 46 var relationViewImplications = relationView.getImpliedRelationViews();
42 var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size()); 47 var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size());
@@ -52,11 +57,25 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
52 relationViewImplication.impliedIndices())); 57 relationViewImplication.impliedIndices()));
53 } 58 }
54 } 59 }
60 var sorts = relationView.getSorts();
61 int arity = relationView.arity();
62 for (int i = 0; i < arity; i++) {
63 var sort = sorts.get(i);
64 if (sort instanceof DataSort<?> dataSort) {
65 var javaTransitiveInstancesKey = new JavaTransitiveInstancesKey(dataSort.type());
66 var javaImplication = new InputKeyImplication(implyingKey, javaTransitiveInstancesKey,
67 List.of(i));
68 inputKeyImplications.add(javaImplication);
69 }
70 }
55 return inputKeyImplications; 71 return inputKeyImplications;
56 } 72 }
57 73
58 @Override 74 @Override
59 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) { 75 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) {
76 if (key instanceof JavaTransitiveInstancesKey) {
77 return Map.of();
78 }
60 var relationView = checkKey(key); 79 var relationView = checkKey(key);
61 var functionalDependencies = relationView.getFunctionalDependencies(); 80 var functionalDependencies = relationView.getFunctionalDependencies();
62 var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size()); 81 var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size());
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..854817b1 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
@@ -54,7 +54,8 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
54 54
55 @Override 55 @Override
56 public boolean isIndexed(IInputKey key, IndexingService service) { 56 public boolean isIndexed(IInputKey key, IndexingService service) {
57 if (key instanceof AnyRelationView relationalKey) { 57 if (key instanceof RelationViewWrapper wrapper) {
58 var relationalKey = wrapper.getWrappedKey();
58 return this.modelUpdateListener.containsRelationView(relationalKey); 59 return this.modelUpdateListener.containsRelationView(relationalKey);
59 } else { 60 } else {
60 return false; 61 return false;
@@ -83,10 +84,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
83 84
84 @Override 85 @Override
85 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) { 86 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
86 var relationViewKey = checkKey(key); 87 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; 88 int result = 0;
91 while (iterator.hasNext()) { 89 while (iterator.hasNext()) {
92 iterator.next(); 90 iterator.next();
@@ -102,13 +100,25 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
102 100
103 @Override 101 @Override
104 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) { 102 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
103 var filteredBySeed = enumerate(key, seedMask, seed);
104 return map(filteredBySeed, Tuples::flatTupleOf);
105 }
106
107 @Override
108 public Iterable<?> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
109 var index = seedMask.getFirstOmittedIndex().orElseThrow(
110 () -> new IllegalArgumentException("Seed mask does not omit a value"));
111 var filteredBySeed = enumerate(key, seedMask, seed);
112 return map(filteredBySeed, array -> array[index]);
113 }
114
115 private Iterable<Object[]> enumerate(IInputKey key, TupleMask seedMask, ITuple seed) {
105 var relationViewKey = checkKey(key); 116 var relationViewKey = checkKey(key);
106 Iterable<Object[]> allObjects = relationViewKey.getAll(model); 117 Iterable<Object[]> allObjects = relationViewKey.getAll(model);
107 Iterable<Object[]> filteredBySeed = filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed)); 118 return filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed));
108 return map(filteredBySeed, Tuples::flatTupleOf);
109 } 119 }
110 120
111 private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) { 121 private static boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) {
112 for (int i = 0; i < seedMask.indices.length; i++) { 122 for (int i = 0; i < seedMask.indices.length; i++) {
113 final Object seedElement = seed.get(i); 123 final Object seedElement = seed.get(i);
114 final Object tupleElement = tuple[seedMask.indices[i]]; 124 final Object tupleElement = tuple[seedMask.indices[i]];
@@ -120,11 +130,6 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
120 } 130 }
121 131
122 @Override 132 @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) { 133 public boolean containsTuple(IInputKey key, ITuple seed) {
129 var relationViewKey = checkKey(key); 134 var relationViewKey = checkKey(key);
130 return relationViewKey.get(model, seed.getElements()); 135 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..2d3dd5a7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java
@@ -0,0 +1,75 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 * SPDX-License-Identifier: EPL-2.0
7 *******************************************************************************/
8package tools.refinery.store.query.viatra.internal.localsearch;
9
10import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
11import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
12import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor;
13
14import java.util.Iterator;
15
16/**
17 * An operation that can be used to enumerate all possible values for a single position based on a constraint
18 * @author Zoltan Ujhelyi, Akos Horvath
19 * @since 2.0
20 */
21abstract class ExtendOperationExecutor<T> implements ISearchOperationExecutor {
22
23 private Iterator<? extends T> it;
24
25 /**
26 * Returns an iterator with the possible options from the current state
27 * @since 2.0
28 */
29 @SuppressWarnings("squid:S1452")
30 protected abstract Iterator<? extends T> getIterator(MatchingFrame frame, ISearchContext context);
31 /**
32 * Updates the frame with the next element of the iterator. Called during {@link #execute(MatchingFrame, ISearchContext)}.
33 *
34 * @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.
35 * @since 2.0
36 */
37 protected abstract boolean fillInValue(T newValue, MatchingFrame frame, ISearchContext context);
38
39 /**
40 * Restores the frame to the state before {@link #fillInValue(Object, MatchingFrame, ISearchContext)}. Called during
41 * {@link #onBacktrack(MatchingFrame, ISearchContext)}.
42 *
43 * @since 2.0
44 */
45 protected abstract void cleanup(MatchingFrame frame, ISearchContext context);
46
47 @Override
48 public void onInitialize(MatchingFrame frame, ISearchContext context) {
49 it = getIterator(frame, context);
50 }
51
52 @Override
53 public void onBacktrack(MatchingFrame frame, ISearchContext context) {
54 it = null;
55
56 }
57
58 /**
59 * Fixed version of {@link org.eclipse.viatra.query.runtime.localsearch.operations.ExtendOperationExecutor#execute}
60 * that handles failed unification of variables correctly.
61 * @param frame The matching frame to extend.
62 * @param context The search context.
63 * @return {@code true} if an extension was found, {@code false} otherwise.
64 */
65 @Override
66 public boolean execute(MatchingFrame frame, ISearchContext context) {
67 while (it.hasNext()) {
68 var newValue = it.next();
69 if (fillInValue(newValue, frame, context)) {
70 return true;
71 }
72 }
73 return false;
74 }
75}
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..aaaece80
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
@@ -0,0 +1,116 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 * SPDX-License-Identifier: EPL-2.0
7 *******************************************************************************/
8package tools.refinery.store.query.viatra.internal.localsearch;
9
10import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
11import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
12import org.eclipse.viatra.query.runtime.localsearch.operations.IPatternMatcherOperation;
13import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation;
14import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation;
15import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
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.VolatileModifiableMaskedTuple;
19
20import java.util.Iterator;
21import java.util.List;
22import java.util.function.Function;
23
24/**
25 * @author Grill Balázs
26 * @since 1.4
27 *
28 */
29public class ExtendPositivePatternCall implements ISearchOperation, IPatternMatcherOperation {
30
31 private class Executor extends ExtendOperationExecutor<Tuple> {
32 private final VolatileModifiableMaskedTuple maskedTuple;
33
34 public Executor() {
35 maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
36 }
37
38 @Override
39 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
40 maskedTuple.updateTuple(frame);
41 IQueryResultProvider matcher = context.getMatcher(information.getCallWithAdornment());
42 return matcher.getAllMatches(information.getParameterMask(), maskedTuple).iterator();
43 }
44
45 /**
46 * @since 2.0
47 */
48 @Override
49 protected boolean fillInValue(Tuple result, MatchingFrame frame, ISearchContext context) {
50 TupleMask mask = information.getFullFrameMask();
51 // The first loop clears out the elements from a possible previous iteration
52 for(int i : information.getFreeParameterIndices()) {
53 mask.set(frame, i, null);
54 }
55 for(int i : information.getFreeParameterIndices()) {
56 Object oldValue = mask.getValue(frame, i);
57 Object valueToFill = result.get(i);
58 if (oldValue != null && !oldValue.equals(valueToFill)){
59 // If the inverse map contains more than one values for the same key, it means that these arguments are unified by the caller.
60 // In this case if the callee assigns different values the frame shall be dropped
61 return false;
62 }
63 mask.set(frame, i, valueToFill);
64 }
65 return true;
66 }
67
68 @Override
69 protected void cleanup(MatchingFrame frame, ISearchContext context) {
70 TupleMask mask = information.getFullFrameMask();
71 for(int i : information.getFreeParameterIndices()){
72 mask.set(frame, i, null);
73 }
74
75 }
76
77 @Override
78 public ISearchOperation getOperation() {
79 return ExtendPositivePatternCall.this;
80 }
81 }
82
83 private final CallInformation information;
84
85 /**
86 * @since 1.7
87 */
88 public ExtendPositivePatternCall(CallInformation information) {
89 this.information = information;
90 }
91
92 @Override
93 public ISearchOperationExecutor createExecutor() {
94 return new Executor();
95 }
96
97 @Override
98 public List<Integer> getVariablePositions() {
99 return information.getVariablePositions();
100 }
101
102 @Override
103 public String toString() {
104 return toString(Object::toString);
105 }
106
107 @Override
108 public String toString(@SuppressWarnings("squid:S4276") Function<Integer, String> variableMapping) {
109 return "extend find " + information.toString(variableMapping);
110 }
111
112 @Override
113 public CallInformation getCallInformation() {
114 return information;
115 }
116}
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..84cab142
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
4import org.eclipse.viatra.query.runtime.localsearch.planner.cost.impl.StatisticsBasedConstraintCostFunction;
5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
6import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
7import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
8import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
9
10import java.util.Optional;
11
12public class FlatCostFunction extends StatisticsBasedConstraintCostFunction {
13 public FlatCostFunction() {
14 // No inverse navigation penalty thanks to relational storage.
15 super(0);
16 }
17
18 @Override
19 public Optional<Long> projectionSize(IConstraintEvaluationContext input, IInputKey supplierKey, TupleMask groupMask, Accuracy requiredAccuracy) {
20 // We always start from an empty model, where every projection is of size 0.
21 // Therefore, projection size estimation is meaningless.
22 return Optional.empty();
23 }
24
25 @Override
26 protected double _calculateCost(TypeConstraint constraint, IConstraintEvaluationContext input) {
27 // Assume a flat cost for each relation. Maybe adjust in the future if we perform indexing?
28 return DEFAULT_COST;
29 }
30}
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..64653658
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
@@ -0,0 +1,136 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 * SPDX-License-Identifier: EPL-2.0
7 *******************************************************************************/
8package tools.refinery.store.query.viatra.internal.localsearch;
9
10import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
11import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
12import org.eclipse.viatra.query.runtime.localsearch.operations.IIteratingSearchOperation;
13import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation;
14import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
15import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
16import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
17import org.eclipse.viatra.query.runtime.matchers.tuple.VolatileMaskedTuple;
18import org.eclipse.viatra.query.runtime.matchers.util.Preconditions;
19
20import java.util.*;
21import java.util.function.Function;
22import java.util.stream.Collectors;
23
24/**
25 * @author Zoltan Ujhelyi
26 * @since 1.7
27 */
28public class GenericTypeExtend implements IIteratingSearchOperation {
29 private class Executor extends ExtendOperationExecutor<Tuple> {
30 private final VolatileMaskedTuple maskedTuple;
31
32 public Executor() {
33 this.maskedTuple = new VolatileMaskedTuple(callMask);
34 }
35
36 @Override
37 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
38 maskedTuple.updateTuple(frame);
39 return context.getRuntimeContext().enumerateTuples(type, indexerMask, maskedTuple).iterator();
40 }
41
42 @Override
43 protected boolean fillInValue(Tuple newTuple, MatchingFrame frame, ISearchContext context) {
44 for (Integer position : unboundVariableIndices) {
45 frame.setValue(position, null);
46 }
47 for (int i = 0; i < positions.length; i++) {
48 Object newValue = newTuple.get(i);
49 Object oldValue = frame.getValue(positions[i]);
50 if (oldValue != null && !Objects.equals(oldValue, newValue)) {
51 // If positions tuple maps more than one values for the same element (e.g. loop), it means that
52 // these arguments are to unified by the caller. In this case if the callee assigns different values
53 // the frame shall be considered a failed match
54 return false;
55 }
56 frame.setValue(positions[i], newValue);
57 }
58 return true;
59 }
60
61 @Override
62 protected void cleanup(MatchingFrame frame, ISearchContext context) {
63 for (Integer position : unboundVariableIndices) {
64 frame.setValue(position, null);
65 }
66 }
67
68 @Override
69 public ISearchOperation getOperation() {
70 return GenericTypeExtend.this;
71 }
72 }
73
74 private final IInputKey type;
75 private final int[] positions;
76 private final List<Integer> positionList;
77 private final Set<Integer> unboundVariableIndices;
78 private final TupleMask indexerMask;
79 private final TupleMask callMask;
80
81 /**
82 *
83 * @param type
84 * the type to execute the extend operation on
85 * @param positions
86 * the parameter positions that represent the variables of the input key
87 * @param unboundVariableIndices
88 * the set of positions that are bound at the start of the operation
89 */
90 public GenericTypeExtend(IInputKey type, int[] positions, TupleMask callMask, TupleMask indexerMask, Set<Integer> unboundVariableIndices) {
91 Preconditions.checkArgument(positions.length == type.getArity(),
92 "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(),
93 type.getArity(), positions.length);
94 List<Integer> modifiablePositionList = new ArrayList<>();
95 for (int position : positions) {
96 modifiablePositionList.add(position);
97 }
98 this.positionList = Collections.unmodifiableList(modifiablePositionList);
99 this.positions = positions;
100 this.type = type;
101
102 this.unboundVariableIndices = unboundVariableIndices;
103 this.indexerMask = indexerMask;
104 this.callMask = callMask;
105 }
106
107 @Override
108 public IInputKey getIteratedInputKey() {
109 return type;
110 }
111
112 @Override
113 public ISearchOperationExecutor createExecutor() {
114 return new Executor();
115 }
116
117 @Override
118 public List<Integer> getVariablePositions() {
119 return positionList;
120 }
121
122 @Override
123 public String toString() {
124 return toString(Object::toString);
125 }
126
127 @Override
128 public String toString(@SuppressWarnings("squid:S4276") Function<Integer, String> variableMapping) {
129 return "extend " + type.getPrettyPrintableName() + "("
130 + positionList.stream()
131 .map(input -> String.format("%s%s", unboundVariableIndices.contains(input) ? "-" : "+", variableMapping.apply(input)))
132 .collect(Collectors.joining(", "))
133 + ")";
134 }
135
136}
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..156eb313
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
@@ -0,0 +1,55 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
4import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints;
6import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider;
7import org.eclipse.viatra.query.runtime.localsearch.plan.SimplePlanProvider;
8import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability;
9import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
10import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
11import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
12import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
13import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
14
15public class RelationalLocalSearchBackendFactory implements IQueryBackendFactory {
16 public static final RelationalLocalSearchBackendFactory INSTANCE = new RelationalLocalSearchBackendFactory();
17
18 private RelationalLocalSearchBackendFactory() {
19 }
20
21 @Override
22 public IQueryBackend create(IQueryBackendContext context) {
23 return new LocalSearchBackend(context) {
24 // Create a new {@link IPlanProvider}, because the original {@link LocalSearchBackend#planProvider} is not
25 // accessible.
26 private final IPlanProvider planProvider = new SimplePlanProvider(context.getLogger());
27
28 @Override
29 protected AbstractLocalSearchResultProvider initializeResultProvider(PQuery query,
30 QueryEvaluationHint hints) {
31 return new RelationalLocalSearchResultProvider(this, context, query, planProvider, hints);
32 }
33
34 @Override
35 public IQueryBackendFactory getFactory() {
36 return RelationalLocalSearchBackendFactory.this;
37 }
38 };
39 }
40
41 @Override
42 public Class<? extends IQueryBackend> getBackendClass() {
43 return LocalSearchBackend.class;
44 }
45
46 @Override
47 public IMatcherCapability calculateRequiredCapability(PQuery pQuery, QueryEvaluationHint queryEvaluationHint) {
48 return LocalSearchHints.parse(queryEvaluationHint);
49 }
50
51 @Override
52 public boolean isCaching() {
53 return false;
54 }
55}
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..be2a38ca
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
4import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints;
6import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider;
7import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.IOperationCompiler;
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
9import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
11
12class RelationalLocalSearchResultProvider extends AbstractLocalSearchResultProvider {
13 public RelationalLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
14 IPlanProvider planProvider, QueryEvaluationHint userHints) {
15 super(backend, context, query, planProvider, userHints);
16 }
17
18 @Override
19 protected IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext,
20 LocalSearchHints configuration) {
21 return new RelationalOperationCompiler(runtimeContext);
22 }
23}
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..b6832b39
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
@@ -0,0 +1,65 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue;
4import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation;
5import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.GenericOperationCompiler;
6import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
7import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
8import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
9import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
10import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
11import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
12import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
13
14import java.util.*;
15
16public class RelationalOperationCompiler extends GenericOperationCompiler {
17 public RelationalOperationCompiler(IQueryRuntimeContext runtimeContext) {
18 super(runtimeContext);
19 }
20
21 @Override
22 protected void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
23 IInputKey inputKey = typeConstraint.getSupplierKey();
24 Tuple tuple = typeConstraint.getVariablesTuple();
25
26 int[] positions = new int[tuple.getSize()];
27 List<Integer> boundVariableIndices = new ArrayList<>();
28 List<Integer> boundVariables = new ArrayList<>();
29 Set<Integer> unboundVariables = new HashSet<>();
30 for (int i = 0; i < tuple.getSize(); i++) {
31 PVariable variable = (PVariable) tuple.get(i);
32 Integer position = variableMapping.get(variable);
33 positions[i] = position;
34 if (variableBindings.get(typeConstraint).contains(position)) {
35 boundVariableIndices.add(i);
36 boundVariables.add(position);
37 } else {
38 unboundVariables.add(position);
39 }
40 }
41 TupleMask indexerMask = TupleMask.fromSelectedIndices(inputKey.getArity(), boundVariableIndices);
42 TupleMask callMask = TupleMask.fromSelectedIndices(variableMapping.size(), boundVariables);
43 // If multiple tuple elements from the indexer should be bound to the same variable, we must use a
44 // {@link GenericTypeExtend} check whether the tuple elements have the same value.
45 if (unboundVariables.size() == 1 && indexerMask.getSize() + 1 == indexerMask.getSourceWidth()) {
46 operations.add(new GenericTypeExtendSingleValue(inputKey, positions, callMask, indexerMask,
47 unboundVariables.iterator().next()));
48 } else {
49 // Use a fixed version of
50 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtend} that handles
51 // failed unification of variables correctly.
52 operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables));
53 }
54 }
55
56 @Override
57 protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
58 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
59 // Use a fixed version of
60 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.extend.ExtendPositivePatternCall} that handles
61 // failed unification of variables correctly.
62 operations.add(new ExtendPositivePatternCall(information));
63 dependencies.add(information.getCallWithAdornment());
64 }
65}
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..4daa14a1
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
4import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer;
5import tools.refinery.store.map.Cursor;
6import tools.refinery.store.tuple.TupleLike;
7
8import java.util.Iterator;
9
10class FunctionalCursor<T> implements Cursor<TupleLike, T> {
11 private final IterableIndexer indexer;
12 private final Iterator<Tuple> iterator;
13 private boolean terminated;
14 private TupleLike key;
15 private T value;
16
17 public FunctionalCursor(IterableIndexer indexer) {
18 this.indexer = indexer;
19 iterator = indexer.getSignatures().iterator();
20 }
21
22 @Override
23 public TupleLike getKey() {
24 return key;
25 }
26
27 @Override
28 public T getValue() {
29 return value;
30 }
31
32 @Override
33 public boolean isTerminated() {
34 return terminated;
35 }
36
37 @Override
38 public boolean move() {
39 if (!terminated && iterator.hasNext()) {
40 var match = iterator.next();
41 key = new ViatraTupleLike(match);
42 value = MatcherUtils.getSingleValue(indexer.get(match));
43 return true;
44 }
45 terminated = true;
46 return false;
47 }
48}
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..6aa45af2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
@@ -0,0 +1,91 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
4import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
5import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
6import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer;
7import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.query.ModelQueryAdapter;
10import tools.refinery.store.query.ResultSet;
11import tools.refinery.store.query.dnf.FunctionalQuery;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
14import tools.refinery.store.tuple.TupleLike;
15
16/**
17 * Directly access the tuples inside a VIATRA pattern matcher.<p>
18 * This class neglects calling
19 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
20 * and
21 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
22 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
23 * implementation for these methods.
24 * Using this class with any other runtime context may lead to undefined behavior.
25 */
26public class FunctionalViatraMatcher<T> implements ResultSet<T> {
27 private final ViatraModelQueryAdapterImpl adapter;
28 private final FunctionalQuery<T> query;
29 private final TupleMask emptyMask;
30 private final TupleMask omitOutputMask;
31 private final IQueryResultProvider backend;
32 private final IterableIndexer omitOutputIndexer;
33
34 public FunctionalViatraMatcher(ViatraModelQueryAdapterImpl adapter, FunctionalQuery<T> query,
35 RawPatternMatcher rawPatternMatcher) {
36 this.adapter = adapter;
37 this.query = query;
38 int arity = query.arity();
39 int arityWithOutput = arity + 1;
40 emptyMask = TupleMask.empty(arityWithOutput);
41 omitOutputMask = TupleMask.omit(arity, arityWithOutput);
42 backend = rawPatternMatcher.getBackend();
43 if (backend instanceof RetePatternMatcher reteBackend) {
44 var maybeIterableOmitOutputIndexer = IndexerUtils.getIndexer(reteBackend, omitOutputMask);
45 if (maybeIterableOmitOutputIndexer instanceof IterableIndexer iterableOmitOutputIndexer) {
46 omitOutputIndexer = iterableOmitOutputIndexer;
47 } else {
48 omitOutputIndexer = null;
49 }
50 } else {
51 omitOutputIndexer = null;
52 }
53 }
54
55 @Override
56 public ModelQueryAdapter getAdapter() {
57 return adapter;
58 }
59
60 @Override
61 public Query<T> getQuery() {
62 return query;
63 }
64
65 @Override
66 public T get(TupleLike parameters) {
67 var tuple = MatcherUtils.toViatraTuple(parameters);
68 if (omitOutputIndexer == null) {
69 return MatcherUtils.getSingleValue(backend.getAllMatches(omitOutputMask, tuple).iterator());
70 } else {
71 return MatcherUtils.getSingleValue(omitOutputIndexer.get(tuple));
72 }
73 }
74
75 @Override
76 public Cursor<TupleLike, T> getAll() {
77 if (omitOutputIndexer == null) {
78 var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf());
79 return new UnsafeFunctionalCursor<>(allMatches.iterator());
80 }
81 return new FunctionalCursor<>(omitOutputIndexer);
82 }
83
84 @Override
85 public int size() {
86 if (omitOutputIndexer == null) {
87 return backend.countMatches(emptyMask, Tuples.staticArityFlatTupleOf());
88 }
89 return omitOutputIndexer.getBucketCount();
90 }
91}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
index 75588b81..55eb8c44 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.query.viatra.internal.pquery; 1package tools.refinery.store.query.viatra.internal.matcher;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 3import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
4import org.eclipse.viatra.query.runtime.rete.index.Indexer; 4import org.eclipse.viatra.query.runtime.rete.index.Indexer;
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..5d4be95d
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
@@ -0,0 +1,50 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
5import org.jetbrains.annotations.Nullable;
6import tools.refinery.store.tuple.Tuple;
7import tools.refinery.store.tuple.TupleLike;
8
9import java.util.Iterator;
10
11final class MatcherUtils {
12 private MatcherUtils() {
13 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
14 }
15
16 public static org.eclipse.viatra.query.runtime.matchers.tuple.Tuple toViatraTuple(TupleLike tuple) {
17 if (tuple instanceof ViatraTupleLike viatraTupleLike) {
18 return viatraTupleLike.wrappedTuple().toImmutable();
19 }
20 int size = tuple.getSize();
21 var array = new Object[size];
22 for (int i = 0; i < size; i++) {
23 var value = tuple.get(i);
24 array[i] = Tuple.of(value);
25 }
26 return Tuples.flatTupleOf(array);
27 }
28
29
30 public static <T> T getSingleValue(@Nullable Iterable<? extends ITuple> tuples) {
31 if (tuples == null) {
32 return null;
33 }
34 return getSingleValue(tuples.iterator());
35 }
36
37 public static <T> T getSingleValue(Iterator<? extends ITuple> iterator) {
38 if (!iterator.hasNext()) {
39 return null;
40 }
41 var match = iterator.next();
42 @SuppressWarnings("unchecked")
43 var result = (T) match.get(match.getSize() - 1);
44 if (iterator.hasNext()) {
45 var input = new OmitOutputViatraTupleLike(match);
46 throw new IllegalStateException("Query is not functional for input tuple: " + input);
47 }
48 return result;
49 }
50}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java
new file mode 100644
index 00000000..bd9301ba
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.tuple.Tuple1;
5import tools.refinery.store.tuple.TupleLike;
6
7record OmitOutputViatraTupleLike(ITuple wrappedTuple) implements TupleLike {
8 @Override
9 public int getSize() {
10 return wrappedTuple.getSize() - 1;
11 }
12
13 @Override
14 public int get(int element) {
15 var wrappedValue = (Tuple1) wrappedTuple.get(element);
16 return wrappedValue.value0();
17 }
18
19 @Override
20 public String toString() {
21 return TupleLike.toString(this);
22 }
23}
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..b3d05967
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
@@ -0,0 +1,15 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
5import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
6
7public class RawPatternMatcher extends GenericPatternMatcher {
8 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
9 super(specification);
10 }
11
12 IQueryResultProvider getBackend() {
13 return backend;
14 }
15}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ResultSetCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
index 5e6d1970..c2dcc565 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ResultSetCursor.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
@@ -1,18 +1,17 @@
1package tools.refinery.store.query.viatra.internal.pquery; 1package tools.refinery.store.query.viatra.internal.matcher;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.map.Cursor; 4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.query.viatra.ViatraTupleLike;
6import tools.refinery.store.tuple.TupleLike; 5import tools.refinery.store.tuple.TupleLike;
7 6
8import java.util.Iterator; 7import java.util.Iterator;
9 8
10class ResultSetCursor implements Cursor<TupleLike, Boolean> { 9class RelationalCursor implements Cursor<TupleLike, Boolean> {
11 private final Iterator<? extends ITuple> tuplesIterator; 10 private final Iterator<? extends ITuple> tuplesIterator;
12 private boolean terminated; 11 private boolean terminated;
13 private TupleLike key; 12 private TupleLike key;
14 13
15 public ResultSetCursor(Iterator<? extends ITuple> tuplesIterator) { 14 public RelationalCursor(Iterator<? extends ITuple> tuplesIterator) {
16 this.tuplesIterator = tuplesIterator; 15 this.tuplesIterator = tuplesIterator;
17 } 16 }
18 17
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..b9bc3f1e
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
@@ -0,0 +1,89 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
4import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
5import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
6import org.eclipse.viatra.query.runtime.rete.index.Indexer;
7import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.Cursors;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.ResultSet;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.query.dnf.RelationalQuery;
14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
15import tools.refinery.store.tuple.TupleLike;
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 RelationalViatraMatcher implements ResultSet<Boolean> {
28 private final ViatraModelQueryAdapterImpl adapter;
29 private final RelationalQuery query;
30 private final TupleMask emptyMask;
31 private final TupleMask identityMask;
32 private final IQueryResultProvider backend;
33 private final Indexer emptyMaskIndexer;
34
35 public RelationalViatraMatcher(ViatraModelQueryAdapterImpl adapter, RelationalQuery query,
36 RawPatternMatcher rawPatternMatcher) {
37 this.adapter = adapter;
38 this.query = query;
39 int arity = query.arity();
40 emptyMask = TupleMask.empty(arity);
41 identityMask = TupleMask.identity(arity);
42 backend = rawPatternMatcher.getBackend();
43 if (backend instanceof RetePatternMatcher reteBackend) {
44 emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, emptyMask);
45 } else {
46 emptyMaskIndexer = null;
47 }
48 }
49
50 @Override
51 public ModelQueryAdapter getAdapter() {
52 return adapter;
53 }
54
55 @Override
56 public Query<Boolean> getQuery() {
57 return query;
58 }
59
60 @Override
61 public Boolean get(TupleLike parameters) {
62 var tuple = MatcherUtils.toViatraTuple(parameters);
63 if (emptyMaskIndexer == null) {
64 return backend.hasMatch(identityMask, tuple);
65 }
66 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
67 return matches != null && matches.contains(tuple);
68 }
69
70 @Override
71 public Cursor<TupleLike, Boolean> getAll() {
72 if (emptyMaskIndexer == null) {
73 var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf());
74 return new RelationalCursor(allMatches.iterator());
75 }
76 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
77 return matches == null ? Cursors.empty() : new RelationalCursor(matches.stream().iterator());
78 }
79
80 @Override
81 public int size() {
82 if (emptyMaskIndexer == null) {
83 return backend.countMatches(emptyMask, Tuples.staticArityFlatTupleOf());
84 }
85 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
86 return matches == null ? 0 : matches.size();
87 }
88
89}
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..6c53fff1
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
@@ -0,0 +1,52 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.tuple.TupleLike;
6
7import java.util.Iterator;
8
9/**
10 * Cursor for a functional result set that iterates over a stream of raw matches and doesn't check whether the
11 * functional dependency of the output on the inputs is obeyed.
12 * @param <T> The output type.
13 */
14class UnsafeFunctionalCursor<T> implements Cursor<TupleLike, T> {
15 private final Iterator<? extends ITuple> tuplesIterator;
16 private boolean terminated;
17 private TupleLike key;
18 private T value;
19
20 public UnsafeFunctionalCursor(Iterator<? extends ITuple> tuplesIterator) {
21 this.tuplesIterator = tuplesIterator;
22 }
23
24 @Override
25 public TupleLike getKey() {
26 return key;
27 }
28
29 @Override
30 public T getValue() {
31 return value;
32 }
33
34 @Override
35 public boolean isTerminated() {
36 return terminated;
37 }
38
39 @Override
40 public boolean move() {
41 if (!terminated && tuplesIterator.hasNext()) {
42 var match = tuplesIterator.next();
43 key = new OmitOutputViatraTupleLike(match);
44 @SuppressWarnings("unchecked")
45 var typedValue = (T) match.get(match.getSize() - 1);
46 value = typedValue;
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/ViatraTupleLike.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java
index 46c28434..76a3e40b 100644
--- 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/internal/matcher/ViatraTupleLike.java
@@ -1,10 +1,10 @@
1package tools.refinery.store.query.viatra; 1package tools.refinery.store.query.viatra.internal.matcher;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.tuple.Tuple1; 4import tools.refinery.store.tuple.Tuple1;
5import tools.refinery.store.tuple.TupleLike; 5import tools.refinery.store.tuple.TupleLike;
6 6
7public record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike { 7record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike {
8 @Override 8 @Override
9 public int getSize() { 9 public int getSize() {
10 return wrappedTuple.getSize(); 10 return wrappedTuple.getSize();
@@ -15,4 +15,9 @@ public record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike {
15 var wrappedValue = (Tuple1) wrappedTuple.get(element); 15 var wrappedValue = (Tuple1) wrappedTuple.get(element);
16 return wrappedValue.value0(); 16 return wrappedValue.value0();
17 } 17 }
18
19 @Override
20 public String toString() {
21 return TupleLike.toString(this);
22 }
18} 23}
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..a80b0f90
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
4import tools.refinery.store.query.term.Term;
5
6class AssumptionEvaluator extends TermEvaluator<Boolean> {
7 public AssumptionEvaluator(Term<Boolean> term) {
8 super(term);
9 }
10
11 @Override
12 public Object evaluateExpression(IValueProvider provider) {
13 var result = super.evaluateExpression(provider);
14 return result == null ? Boolean.FALSE : result;
15 }
16}
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
index 201e0ed0..7afeb977 100644
--- 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
@@ -1,45 +1,44 @@
1package tools.refinery.store.query.viatra.internal.pquery; 1package tools.refinery.store.query.viatra.internal.pquery;
2 2
3import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 4import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 5import 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.PBody;
6import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; 7import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
8import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator;
9import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
7import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation; 10import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation;
8import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; 11import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.*;
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; 12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue; 13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue;
14import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; 14import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; 15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
16import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 16import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
17import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 17import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
19import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 19import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
20import tools.refinery.store.query.Dnf; 20import tools.refinery.store.query.dnf.Dnf;
21import tools.refinery.store.query.DnfClause; 21import tools.refinery.store.query.dnf.DnfClause;
22import tools.refinery.store.query.DnfUtils;
23import tools.refinery.store.query.Variable;
24import tools.refinery.store.query.literal.*; 22import tools.refinery.store.query.literal.*;
23import tools.refinery.store.query.term.ConstantTerm;
24import tools.refinery.store.query.term.StatefulAggregator;
25import tools.refinery.store.query.term.StatelessAggregator;
26import tools.refinery.store.query.term.Variable;
25import tools.refinery.store.query.view.AnyRelationView; 27import tools.refinery.store.query.view.AnyRelationView;
26import tools.refinery.store.util.CycleDetectingMapper; 28import tools.refinery.store.util.CycleDetectingMapper;
27 29
28import java.util.*; 30import java.util.*;
29import java.util.function.Function; 31import java.util.function.Function;
32import java.util.stream.Collectors;
30 33
31public class Dnf2PQuery { 34public class Dnf2PQuery {
32 private static final Object P_CONSTRAINT_LOCK = new Object(); 35 private static final Object P_CONSTRAINT_LOCK = new Object();
33
34 private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name, 36 private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name,
35 this::doTranslate); 37 this::doTranslate);
36 38 private final QueryWrapperFactory wrapperFactory = new QueryWrapperFactory(this);
37 private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>(); 39 private final Map<Dnf, QueryEvaluationHint> hintOverrides = new LinkedHashMap<>();
38
39 private final Map<AnyRelationView, RawPQuery> view2EmbeddedMap = new HashMap<>();
40
41 private Function<Dnf, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null, 40 private Function<Dnf, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null,
42 QueryEvaluationHint.BackendRequirement.UNSPECIFIED); 41 (IQueryBackendFactory) null);
43 42
44 public void setComputeHint(Function<Dnf, QueryEvaluationHint> computeHint) { 43 public void setComputeHint(Function<Dnf, QueryEvaluationHint> computeHint) {
45 this.computeHint = computeHint; 44 this.computeHint = computeHint;
@@ -50,16 +49,33 @@ public class Dnf2PQuery {
50 } 49 }
51 50
52 public Map<AnyRelationView, IInputKey> getRelationViews() { 51 public Map<AnyRelationView, IInputKey> getRelationViews() {
53 return Collections.unmodifiableMap(view2WrapperMap); 52 return wrapperFactory.getRelationViews();
54 } 53 }
55 54
56 public RawPQuery getAlreadyTranslated(Dnf dnfQuery) { 55 public void hint(Dnf dnf, QueryEvaluationHint hint) {
57 return mapper.getAlreadyMapped(dnfQuery); 56 hintOverrides.compute(dnf, (ignoredKey, existingHint) ->
57 existingHint == null ? hint : existingHint.overrideBy(hint));
58 }
59
60 private QueryEvaluationHint consumeHint(Dnf dnf) {
61 var defaultHint = computeHint.apply(dnf);
62 var existingHint = hintOverrides.remove(dnf);
63 return defaultHint.overrideBy(existingHint);
64 }
65
66 public void assertNoUnusedHints() {
67 if (hintOverrides.isEmpty()) {
68 return;
69 }
70 var unusedHints = hintOverrides.keySet().stream().map(Dnf::name).collect(Collectors.joining(", "));
71 throw new IllegalStateException(
72 "Unused query evaluation hints for %s. Hints must be set before a query is added to the engine"
73 .formatted(unusedHints));
58 } 74 }
59 75
60 private RawPQuery doTranslate(Dnf dnfQuery) { 76 private RawPQuery doTranslate(Dnf dnfQuery) {
61 var pQuery = new RawPQuery(dnfQuery.getUniqueName()); 77 var pQuery = new RawPQuery(dnfQuery.getUniqueName());
62 pQuery.setEvaluationHints(computeHint.apply(dnfQuery)); 78 pQuery.setEvaluationHints(consumeHint(dnfQuery));
63 79
64 Map<Variable, PParameter> parameters = new HashMap<>(); 80 Map<Variable, PParameter> parameters = new HashMap<>();
65 for (Variable variable : dnfQuery.getParameters()) { 81 for (Variable variable : dnfQuery.getParameters()) {
@@ -97,7 +113,7 @@ public class Dnf2PQuery {
97 body.setSymbolicParameters(symbolicParameters); 113 body.setSymbolicParameters(symbolicParameters);
98 pQuery.addBody(body); 114 pQuery.addBody(body);
99 for (Literal literal : clause.literals()) { 115 for (Literal literal : clause.literals()) {
100 translateLiteral(literal, body); 116 translateLiteral(literal, clause, body);
101 } 117 }
102 } 118 }
103 } 119 }
@@ -105,15 +121,21 @@ public class Dnf2PQuery {
105 return pQuery; 121 return pQuery;
106 } 122 }
107 123
108 private void translateLiteral(Literal literal, PBody body) { 124 private void translateLiteral(Literal literal, DnfClause clause, PBody body) {
109 if (literal instanceof EquivalenceLiteral equivalenceLiteral) { 125 if (literal instanceof EquivalenceLiteral equivalenceLiteral) {
110 translateEquivalenceLiteral(equivalenceLiteral, body); 126 translateEquivalenceLiteral(equivalenceLiteral, body);
111 } else if (literal instanceof RelationViewLiteral relationViewLiteral) { 127 } else if (literal instanceof CallLiteral callLiteral) {
112 translateRelationViewLiteral(relationViewLiteral, body); 128 translateCallLiteral(callLiteral, clause, body);
113 } else if (literal instanceof DnfCallLiteral dnfCallLiteral) {
114 translateDnfCallLiteral(dnfCallLiteral, body);
115 } else if (literal instanceof ConstantLiteral constantLiteral) { 129 } else if (literal instanceof ConstantLiteral constantLiteral) {
116 translateConstantLiteral(constantLiteral, body); 130 translateConstantLiteral(constantLiteral, body);
131 } else if (literal instanceof AssignLiteral<?> assignLiteral) {
132 translateAssignLiteral(assignLiteral, body);
133 } else if (literal instanceof AssumeLiteral assumeLiteral) {
134 translateAssumeLiteral(assumeLiteral, body);
135 } else if (literal instanceof CountLiteral countLiteral) {
136 translateCountLiteral(countLiteral, clause, body);
137 } else if (literal instanceof AggregationLiteral<?, ?> aggregationLiteral) {
138 translateAggregationLiteral(aggregationLiteral, clause, body);
117 } else { 139 } else {
118 throw new IllegalArgumentException("Unknown literal: " + literal.toString()); 140 throw new IllegalArgumentException("Unknown literal: " + literal.toString());
119 } 141 }
@@ -129,20 +151,43 @@ public class Dnf2PQuery {
129 } 151 }
130 } 152 }
131 153
132 private void translateRelationViewLiteral(RelationViewLiteral relationViewLiteral, PBody body) { 154 private void translateCallLiteral(CallLiteral callLiteral, DnfClause clause, PBody body) {
133 var substitution = translateSubstitution(relationViewLiteral.getArguments(), body); 155 var polarity = callLiteral.getPolarity();
134 var polarity = relationViewLiteral.getPolarity(); 156 switch (polarity) {
135 var relationView = relationViewLiteral.getTarget(); 157 case POSITIVE -> {
136 if (polarity == CallPolarity.POSITIVE) { 158 var substitution = translateSubstitution(callLiteral.getArguments(), body);
137 new TypeConstraint(body, substitution, wrapView(relationView)); 159 var constraint = callLiteral.getTarget();
138 } else { 160 if (constraint instanceof Dnf dnf) {
139 var embeddedPQuery = translateEmbeddedRelationViewPQuery(relationView); 161 var pattern = translate(dnf);
140 switch (polarity) { 162 new PositivePatternCall(body, substitution, pattern);
141 case TRANSITIVE -> new BinaryTransitiveClosure(body, substitution, embeddedPQuery); 163 } else if (constraint instanceof AnyRelationView relationView) {
142 case NEGATIVE -> new NegativePatternCall(body, substitution, embeddedPQuery); 164 var inputKey = wrapperFactory.getInputKey(relationView);
143 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity); 165 new TypeConstraint(body, substitution, inputKey);
166 } else {
167 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
144 } 168 }
145 } 169 }
170 case TRANSITIVE -> {
171 var substitution = translateSubstitution(callLiteral.getArguments(), body);
172 var constraint = callLiteral.getTarget();
173 PQuery pattern;
174 if (constraint instanceof Dnf dnf) {
175 pattern = translate(dnf);
176 } else if (constraint instanceof AnyRelationView relationView) {
177 pattern = wrapperFactory.wrapRelationViewIdentityArguments(relationView);
178 } else {
179 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
180 }
181 new BinaryTransitiveClosure(body, substitution, pattern);
182 }
183 case NEGATIVE -> {
184 var wrappedCall = wrapperFactory.maybeWrapConstraint(callLiteral, clause);
185 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
186 var pattern = wrappedCall.pattern();
187 new NegativePatternCall(body, substitution, pattern);
188 }
189 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
190 }
146 } 191 }
147 192
148 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) { 193 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) {
@@ -155,51 +200,57 @@ public class Dnf2PQuery {
155 return Tuples.flatTupleOf(variables); 200 return Tuples.flatTupleOf(variables);
156 } 201 }
157 202
158 private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) { 203 private void translateConstantLiteral(ConstantLiteral constantLiteral, PBody body) {
159 return view2EmbeddedMap.computeIfAbsent(relationView, this::doTranslateEmbeddedRelationViewPQuery); 204 var variable = body.getOrCreateVariableByName(constantLiteral.variable().getUniqueName());
205 new ConstantValue(body, variable, constantLiteral.nodeId());
160 } 206 }
161 207
162 private RawPQuery doTranslateEmbeddedRelationViewPQuery(AnyRelationView relationView) { 208 private <T> void translateAssignLiteral(AssignLiteral<T> assignLiteral, PBody body) {
163 var embeddedPQuery = new RawPQuery(DnfUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED); 209 var variable = body.getOrCreateVariableByName(assignLiteral.variable().getUniqueName());
164 var body = new PBody(embeddedPQuery); 210 var term = assignLiteral.term();
165 int arity = relationView.arity(); 211 if (term instanceof ConstantTerm<T> constantTerm) {
166 var parameters = new ArrayList<PParameter>(arity); 212 new ConstantValue(body, variable, constantTerm.getValue());
167 var arguments = new Object[arity]; 213 } else {
168 var symbolicParameters = new ArrayList<ExportedParameter>(arity); 214 var evaluator = new TermEvaluator<>(term);
169 for (int i = 0; i < arity; i++) { 215 new ExpressionEvaluation(body, evaluator, variable);
170 var parameterName = "p" + i;
171 var parameter = new PParameter(parameterName);
172 parameters.add(parameter);
173 var variable = body.getOrCreateVariableByName(parameterName);
174 arguments[i] = variable;
175 symbolicParameters.add(new ExportedParameter(body, variable, parameter));
176 } 216 }
177 embeddedPQuery.setParameters(parameters);
178 body.setSymbolicParameters(symbolicParameters);
179 var argumentTuple = Tuples.flatTupleOf(arguments);
180 new TypeConstraint(body, argumentTuple, wrapView(relationView));
181 embeddedPQuery.addBody(body);
182 return embeddedPQuery;
183 } 217 }
184 218
185 private RelationViewWrapper wrapView(AnyRelationView relationView) { 219 private void translateAssumeLiteral(AssumeLiteral assumeLiteral, PBody body) {
186 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new); 220 var evaluator = new AssumptionEvaluator(assumeLiteral.term());
221 new ExpressionEvaluation(body, evaluator, null);
187 } 222 }
188 223
189 private void translateDnfCallLiteral(DnfCallLiteral dnfCallLiteral, PBody body) { 224 private void translateCountLiteral(CountLiteral countLiteral, DnfClause clause, PBody body) {
190 var variablesTuple = translateSubstitution(dnfCallLiteral.getArguments(), body); 225 var wrappedCall = wrapperFactory.maybeWrapConstraint(countLiteral, clause);
191 var translatedReferred = translate(dnfCallLiteral.getTarget()); 226 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
192 var polarity = dnfCallLiteral.getPolarity(); 227 var resultVariable = body.getOrCreateVariableByName(countLiteral.getResultVariable().getUniqueName());
193 switch (polarity) { 228 new PatternMatchCounter(body, substitution, wrappedCall.pattern(), resultVariable);
194 case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred);
195 case TRANSITIVE -> new BinaryTransitiveClosure(body, variablesTuple, translatedReferred);
196 case NEGATIVE -> new NegativePatternCall(body, variablesTuple, translatedReferred);
197 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
198 }
199 } 229 }
200 230
201 private void translateConstantLiteral(ConstantLiteral constantLiteral, PBody body) { 231 private <R, T> void translateAggregationLiteral(AggregationLiteral<R, T> aggregationLiteral, DnfClause clause,
202 var variable = body.getOrCreateVariableByName(constantLiteral.variable().getUniqueName()); 232 PBody body) {
203 new ConstantValue(body, variable, constantLiteral.nodeId()); 233 var aggregator = aggregationLiteral.getAggregator();
234 IMultisetAggregationOperator<T, ?, R> aggregationOperator;
235 if (aggregator instanceof StatelessAggregator<R, T> statelessAggregator) {
236 aggregationOperator = new StatelessMultisetAggregator<>(statelessAggregator);
237 } else if (aggregator instanceof StatefulAggregator<R, T> statefulAggregator) {
238 aggregationOperator = new StatefulMultisetAggregator<>(statefulAggregator);
239 } else {
240 throw new IllegalArgumentException("Unknown aggregator: " + aggregator);
241 }
242 var wrappedCall = wrapperFactory.maybeWrapConstraint(aggregationLiteral, clause);
243 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
244 var inputVariable = body.getOrCreateVariableByName(aggregationLiteral.getInputVariable().getUniqueName());
245 var aggregatedColumn = substitution.invertIndex().get(inputVariable);
246 if (aggregatedColumn == null) {
247 throw new IllegalStateException("Input variable %s not found in substitution %s".formatted(inputVariable,
248 substitution));
249 }
250 var boundAggregator = new BoundAggregator(aggregationOperator, aggregator.getInputType(),
251 aggregator.getResultType());
252 var resultVariable = body.getOrCreateVariableByName(aggregationLiteral.getResultVariable().getUniqueName());
253 new AggregatorConstraint(boundAggregator, body, substitution, wrappedCall.pattern(), resultVariable,
254 aggregatedColumn);
204 } 255 }
205} 256}
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..24ae5196
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
@@ -0,0 +1,173 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
5import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
6import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
7import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
8import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
11import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
12import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
13import tools.refinery.store.query.Constraint;
14import tools.refinery.store.query.dnf.Dnf;
15import tools.refinery.store.query.dnf.DnfClause;
16import tools.refinery.store.query.dnf.DnfUtils;
17import tools.refinery.store.query.literal.AbstractCallLiteral;
18import tools.refinery.store.query.term.Variable;
19import tools.refinery.store.query.view.AnyRelationView;
20import tools.refinery.store.query.view.RelationView;
21import tools.refinery.store.util.CycleDetectingMapper;
22
23import java.util.*;
24import java.util.function.ToIntFunction;
25
26class QueryWrapperFactory {
27 private final Dnf2PQuery dnf2PQuery;
28 private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>();
29 private final CycleDetectingMapper<RemappedConstraint, RawPQuery> wrapConstraint = new CycleDetectingMapper<>(
30 RemappedConstraint::toString, this::doWrapConstraint);
31
32 QueryWrapperFactory(Dnf2PQuery dnf2PQuery) {
33 this.dnf2PQuery = dnf2PQuery;
34 }
35
36 public PQuery wrapRelationViewIdentityArguments(AnyRelationView relationView) {
37 var identity = new int[relationView.arity()];
38 for (int i = 0; i < identity.length; i++) {
39 identity[i] = i;
40 }
41 return maybeWrapConstraint(relationView, identity);
42 }
43 public WrappedCall maybeWrapConstraint(AbstractCallLiteral callLiteral, DnfClause clause) {
44 var arguments = callLiteral.getArguments();
45 int arity = arguments.size();
46 var remappedParameters = new int[arity];
47 var boundVariables = clause.boundVariables();
48 var unboundVariableIndices = new HashMap<Variable, Integer>();
49 var appendVariable = new VariableAppender();
50 for (int i = 0; i < arity; i++) {
51 var variable = arguments.get(i);
52 if (boundVariables.contains(variable)) {
53 // Do not join bound variable to make sure that the embedded pattern stays as general as possible.
54 remappedParameters[i] = appendVariable.applyAsInt(variable);
55 } else {
56 remappedParameters[i] = unboundVariableIndices.computeIfAbsent(variable, appendVariable::applyAsInt);
57 }
58 }
59 var pattern = maybeWrapConstraint(callLiteral.getTarget(), remappedParameters);
60 return new WrappedCall(pattern, appendVariable.getRemappedArguments());
61 }
62
63 private PQuery maybeWrapConstraint(Constraint constraint, int[] remappedParameters) {
64 if (remappedParameters.length != constraint.arity()) {
65 throw new IllegalArgumentException("Constraint %s expected %d parameters, but got %d parameters".formatted(
66 constraint, constraint.arity(), remappedParameters.length));
67 }
68 if (constraint instanceof Dnf dnf && isIdentity(remappedParameters)) {
69 return dnf2PQuery.translate(dnf);
70 }
71 return wrapConstraint.map(new RemappedConstraint(constraint, remappedParameters));
72 }
73
74 private static boolean isIdentity(int[] remappedParameters) {
75 for (int i = 0; i < remappedParameters.length; i++) {
76 if (remappedParameters[i] != i) {
77 return false;
78 }
79 }
80 return true;
81 }
82
83 private RawPQuery doWrapConstraint(RemappedConstraint remappedConstraint) {
84 var constraint = remappedConstraint.constraint();
85 var remappedParameters = remappedConstraint.remappedParameters();
86
87 var embeddedPQuery = new RawPQuery(DnfUtils.generateUniqueName(constraint.name()), PVisibility.EMBEDDED);
88 var body = new PBody(embeddedPQuery);
89 int arity = Arrays.stream(remappedParameters).max().orElse(-1) + 1;
90 var parameters = new ArrayList<PParameter>(arity);
91 var parameterVariables = new PVariable[arity];
92 var symbolicParameters = new ArrayList<ExportedParameter>(arity);
93 for (int i = 0; i < arity; i++) {
94 var parameterName = "p" + i;
95 var parameter = new PParameter(parameterName);
96 parameters.add(parameter);
97 var variable = body.getOrCreateVariableByName(parameterName);
98 parameterVariables[i] = variable;
99 symbolicParameters.add(new ExportedParameter(body, variable, parameter));
100 }
101 embeddedPQuery.setParameters(parameters);
102 body.setSymbolicParameters(symbolicParameters);
103
104 var arguments = new Object[remappedParameters.length];
105 for (int i = 0; i < remappedParameters.length; i++) {
106 arguments[i] = parameterVariables[remappedParameters[i]];
107 }
108 var argumentTuple = Tuples.flatTupleOf(arguments);
109
110 if (constraint instanceof RelationView<?> relationView) {
111 new TypeConstraint(body, argumentTuple, getInputKey(relationView));
112 } else if (constraint instanceof Dnf dnf) {
113 var calledPQuery = dnf2PQuery.translate(dnf);
114 new PositivePatternCall(body, argumentTuple, calledPQuery);
115 } else {
116 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
117 }
118
119 embeddedPQuery.addBody(body);
120 return embeddedPQuery;
121 }
122
123 public IInputKey getInputKey(AnyRelationView relationView) {
124 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new);
125 }
126
127 public Map<AnyRelationView, IInputKey> getRelationViews() {
128 return Collections.unmodifiableMap(view2WrapperMap);
129 }
130
131 public record WrappedCall(PQuery pattern, List<Variable> remappedArguments) {
132 }
133
134 private static class VariableAppender implements ToIntFunction<Variable> {
135 private final List<Variable> remappedArguments = new ArrayList<>();
136 private int nextIndex = 0;
137
138 @Override
139 public int applyAsInt(Variable variable) {
140 remappedArguments.add(variable);
141 int index = nextIndex;
142 nextIndex++;
143 return index;
144 }
145
146 public List<Variable> getRemappedArguments() {
147 return remappedArguments;
148 }
149 }
150
151 private record RemappedConstraint(Constraint constraint, int[] remappedParameters) {
152 @Override
153 public boolean equals(Object o) {
154 if (this == o) return true;
155 if (o == null || getClass() != o.getClass()) return false;
156 RemappedConstraint that = (RemappedConstraint) o;
157 return constraint.equals(that.constraint) && Arrays.equals(remappedParameters, that.remappedParameters);
158 }
159
160 @Override
161 public int hashCode() {
162 int result = Objects.hash(constraint);
163 result = 31 * result + Arrays.hashCode(remappedParameters);
164 return result;
165 }
166
167 @Override
168 public String toString() {
169 return "RemappedConstraint{constraint=%s, remappedParameters=%s}".formatted(constraint,
170 Arrays.toString(remappedParameters));
171 }
172 }
173}
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..aad4ba3c 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
@@ -9,6 +9,7 @@ import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
11import tools.refinery.store.query.viatra.internal.RelationalScope; 11import tools.refinery.store.query.viatra.internal.RelationalScope;
12import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
12 13
13import java.util.LinkedHashSet; 14import java.util.LinkedHashSet;
14import java.util.List; 15import 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 5924ff15..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
+++ /dev/null
@@ -1,93 +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 org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
6import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability;
7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
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.ResultSet;
15import tools.refinery.store.query.viatra.ViatraTupleLike;
16import tools.refinery.store.tuple.Tuple;
17import tools.refinery.store.tuple.TupleLike;
18
19/**
20 * Directly access the tuples inside a VIATRA pattern matcher.<p>
21 * This class neglects calling
22 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
23 * and
24 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
25 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
26 * implementation for these methods.
27 * Using this class with any other runtime context may lead to undefined behavior.
28 */
29public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet {
30 private final Object[] empty;
31 private final TupleMask identityMask;
32 private Indexer emptyMaskIndexer;
33
34 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
35 super(specification);
36 var arity = specification.getParameterNames().size();
37 empty = new Object[arity];
38 identityMask = TupleMask.identity(arity);
39 }
40
41 @Override
42 protected void setBackend(ViatraQueryEngine engine, IQueryResultProvider resultProvider,
43 IMatcherCapability capabilities) {
44 super.setBackend(engine, resultProvider, capabilities);
45 if (resultProvider instanceof RetePatternMatcher reteBackend) {
46 emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, TupleMask.empty(identityMask.sourceWidth));
47 }
48 }
49
50 @Override
51 public boolean hasResult(TupleLike parameters) {
52 org.eclipse.viatra.query.runtime.matchers.tuple.Tuple tuple;
53 if (parameters instanceof ViatraTupleLike viatraTupleLike) {
54 tuple = viatraTupleLike.wrappedTuple().toImmutable();
55 } else {
56 var parametersArray = toParametersArray(parameters);
57 tuple = Tuples.flatTupleOf(parametersArray);
58 }
59 if (emptyMaskIndexer == null) {
60 return backend.hasMatch(identityMask, tuple);
61 }
62 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
63 return matches != null && matches.contains(tuple);
64 }
65
66 @Override
67 public Cursor<TupleLike, Boolean> allResults() {
68 if (emptyMaskIndexer == null) {
69 return new ResultSetCursor(backend.getAllMatches(empty).iterator());
70 }
71 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
72 return matches == null ? Cursors.empty() : new ResultSetCursor(matches.stream().iterator());
73 }
74
75 @Override
76 public int countResults() {
77 if (emptyMaskIndexer == null) {
78 return backend.countMatches(empty);
79 }
80 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
81 return matches == null ? 0 : matches.size();
82 }
83
84 private Object[] toParametersArray(TupleLike tuple) {
85 int size = tuple.getSize();
86 var array = new Object[size];
87 for (int i = 0; i < size; i++) {
88 var value = tuple.get(i);
89 array[i] = Tuple.of(value);
90 }
91 return array;
92 }
93}
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/RelationViewWrapper.java
index c442add8..48bf558d 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/RelationViewWrapper.java
@@ -27,4 +27,9 @@ public class RelationViewWrapper extends BaseInputKeyWrapper<AnyRelationView> {
27 public boolean isEnumerable() { 27 public boolean isEnumerable() {
28 return true; 28 return true;
29 } 29 }
30
31 @Override
32 public String toString() {
33 return "RelationViewWrapper{wrappedKey=%s}".formatted(wrappedKey);
34 }
30} 35}
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..2798a252
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
@@ -0,0 +1,60 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
4import tools.refinery.store.query.term.StatefulAggregate;
5import tools.refinery.store.query.term.StatefulAggregator;
6
7import java.util.stream.Stream;
8
9record StatefulMultisetAggregator<R, T>(StatefulAggregator<R, T> aggregator)
10 implements IMultisetAggregationOperator<T, StatefulAggregate<R, T>, R> {
11 @Override
12 public String getShortDescription() {
13 return getName();
14 }
15
16 @Override
17 public String getName() {
18 return aggregator.toString();
19 }
20
21 @Override
22 public StatefulAggregate<R, T> createNeutral() {
23 return aggregator.createEmptyAggregate();
24 }
25
26 @Override
27 public boolean isNeutral(StatefulAggregate<R, T> result) {
28 return result.isEmpty();
29 }
30
31 @Override
32 public StatefulAggregate<R, T> update(StatefulAggregate<R, T> oldResult, T updateValue, boolean isInsertion) {
33 if (isInsertion) {
34 oldResult.add(updateValue);
35 } else {
36 oldResult.remove(updateValue);
37 }
38 return oldResult;
39 }
40
41 @Override
42 public R getAggregate(StatefulAggregate<R, T> result) {
43 return result.getResult();
44 }
45
46 @Override
47 public R aggregateStream(Stream<T> stream) {
48 return aggregator.aggregateStream(stream);
49 }
50
51 @Override
52 public StatefulAggregate<R, T> clone(StatefulAggregate<R, T> original) {
53 return original.deepCopy();
54 }
55
56 @Override
57 public boolean contains(T value, StatefulAggregate<R, T> accumulator) {
58 return accumulator.contains(value);
59 }
60}
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..7cc71ee9
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
@@ -0,0 +1,50 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
4import tools.refinery.store.query.term.StatelessAggregator;
5
6import java.util.stream.Stream;
7
8record StatelessMultisetAggregator<R, T>(StatelessAggregator<R, T> aggregator)
9 implements IMultisetAggregationOperator<T, R, R> {
10 @Override
11 public String getShortDescription() {
12 return getName();
13 }
14
15 @Override
16 public String getName() {
17 return aggregator.toString();
18 }
19
20 @Override
21 public R createNeutral() {
22 return aggregator.getEmptyResult();
23 }
24
25 @Override
26 public boolean isNeutral(R result) {
27 return createNeutral().equals(result);
28 }
29
30 @Override
31 public R update(R oldResult, T updateValue, boolean isInsertion) {
32 return isInsertion ? aggregator.add(oldResult, updateValue) : aggregator.remove(oldResult, updateValue);
33 }
34
35 @Override
36 public R getAggregate(R result) {
37 return result;
38 }
39
40 @Override
41 public R clone(R original) {
42 // Aggregate result is immutable.
43 return original;
44 }
45
46 @Override
47 public R aggregateStream(Stream<T> stream) {
48 return aggregator.aggregateStream(stream);
49 }
50}
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..ab123c50
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
@@ -0,0 +1,32 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator;
4import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
5import tools.refinery.store.query.term.Term;
6import tools.refinery.store.query.term.Variable;
7
8import java.util.stream.Collectors;
9
10class TermEvaluator<T> implements IExpressionEvaluator {
11 private final Term<T> term;
12
13 public TermEvaluator(Term<T> term) {
14 this.term = term;
15 }
16
17 @Override
18 public String getShortDescription() {
19 return term.toString();
20 }
21
22 @Override
23 public Iterable<String> getInputParameterNames() {
24 return term.getInputVariables().stream().map(Variable::getUniqueName).collect(Collectors.toUnmodifiableSet());
25 }
26
27 @Override
28 public Object evaluateExpression(IValueProvider provider) {
29 var valuation = new ValueProviderBasedValuation(provider);
30 return term.evaluate(valuation);
31 }
32}
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..30c2fce7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
@@ -0,0 +1,14 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.valuation.Valuation;
6
7public record ValueProviderBasedValuation(IValueProvider valueProvider) implements Valuation {
8 @Override
9 public <T> T getValue(DataVariable<T> variable) {
10 @SuppressWarnings("unchecked")
11 var value = (T) valueProvider.getValue(variable.getUniqueName());
12 return value;
13 }
14}
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..fc935aa6 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
@@ -22,10 +22,9 @@ public class ModelUpdateListener {
22 } 22 }
23 23
24 private <T> void registerView(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) { 24 private <T> void registerView(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) {
25 var listener = RelationViewUpdateListener.of(adapter, relationView);
26 var model = adapter.getModel(); 25 var model = adapter.getModel();
27 var interpretation = model.getInterpretation(relationView.getSymbol()); 26 var interpretation = model.getInterpretation(relationView.getSymbol());
28 interpretation.addListener(listener, true); 27 var listener = RelationViewUpdateListener.of(adapter, relationView, interpretation);
29 relationViewUpdateListeners.put(relationView, listener); 28 relationViewUpdateListeners.put(relationView, listener);
30 } 29 }
31 30
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
index bf6b4197..5e5f60e3 100644
--- 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
@@ -4,6 +4,7 @@ import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; 4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
7import tools.refinery.store.model.Interpretation;
7import tools.refinery.store.model.InterpretationListener; 8import tools.refinery.store.model.InterpretationListener;
8import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 9import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
9import tools.refinery.store.query.view.RelationView; 10import tools.refinery.store.query.view.RelationView;
@@ -14,18 +15,27 @@ import java.util.List;
14 15
15public abstract class RelationViewUpdateListener<T> implements InterpretationListener<T> { 16public abstract class RelationViewUpdateListener<T> implements InterpretationListener<T> {
16 private final ViatraModelQueryAdapterImpl adapter; 17 private final ViatraModelQueryAdapterImpl adapter;
18 private final Interpretation<T> interpretation;
17 private final List<RelationViewFilter> filters = new ArrayList<>(); 19 private final List<RelationViewFilter> filters = new ArrayList<>();
18 20
19 protected RelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter) { 21 protected RelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, Interpretation<T> interpretation) {
20 this.adapter = adapter; 22 this.adapter = adapter;
23 this.interpretation = interpretation;
21 } 24 }
22 25
23 public void addFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) { 26 public void addFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
27 if (filters.isEmpty()) {
28 // First filter to be added, from now on we have to subscribe to model updates.
29 interpretation.addListener(this, true);
30 }
24 filters.add(new RelationViewFilter(inputKey, seed, listener)); 31 filters.add(new RelationViewFilter(inputKey, seed, listener));
25 } 32 }
26 33
27 public void removeFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) { 34 public void removeFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
28 filters.remove(new RelationViewFilter(inputKey, seed, listener)); 35 if (filters.remove(new RelationViewFilter(inputKey, seed, listener)) && filters.isEmpty()) {
36 // Last listener to be added, we don't have be subscribed to model updates anymore.
37 interpretation.removeListener(this);
38 }
29 } 39 }
30 40
31 protected void processUpdate(Tuple tuple, boolean isInsertion) { 41 protected void processUpdate(Tuple tuple, boolean isInsertion) {
@@ -39,10 +49,12 @@ public abstract class RelationViewUpdateListener<T> implements InterpretationLis
39 } 49 }
40 50
41 public static <T> RelationViewUpdateListener<T> of(ViatraModelQueryAdapterImpl adapter, 51 public static <T> RelationViewUpdateListener<T> of(ViatraModelQueryAdapterImpl adapter,
42 RelationView<T> relationView) { 52 RelationView<T> relationView,
53 Interpretation<T> interpretation) {
43 if (relationView instanceof TuplePreservingRelationView<T> tuplePreservingRelationView) { 54 if (relationView instanceof TuplePreservingRelationView<T> tuplePreservingRelationView) {
44 return new TuplePreservingRelationViewUpdateListener<>(adapter, tuplePreservingRelationView); 55 return new TuplePreservingRelationViewUpdateListener<>(adapter, tuplePreservingRelationView,
56 interpretation);
45 } 57 }
46 return new TupleChangingRelationViewUpdateListener<>(adapter, relationView); 58 return new TupleChangingRelationViewUpdateListener<>(adapter, relationView, interpretation);
47 } 59 }
48} 60}
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
index 14142884..0f6ed3b3 100644
--- 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
@@ -1,6 +1,7 @@
1package tools.refinery.store.query.viatra.internal.update; 1package tools.refinery.store.query.viatra.internal.update;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
4import tools.refinery.store.model.Interpretation;
4import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 5import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
5import tools.refinery.store.query.view.RelationView; 6import tools.refinery.store.query.view.RelationView;
6import tools.refinery.store.tuple.Tuple; 7import tools.refinery.store.tuple.Tuple;
@@ -10,8 +11,9 @@ import java.util.Arrays;
10public class TupleChangingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> { 11public class TupleChangingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> {
11 private final RelationView<T> relationView; 12 private final RelationView<T> relationView;
12 13
13 TupleChangingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) { 14 TupleChangingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView,
14 super(adapter); 15 Interpretation<T> interpretation) {
16 super(adapter, interpretation);
15 this.relationView = relationView; 17 this.relationView = relationView;
16 } 18 }
17 19
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/TuplePreservingRelationViewUpdateListener.java
index 288e018a..91e9371b 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/TuplePreservingRelationViewUpdateListener.java
@@ -1,6 +1,7 @@
1package tools.refinery.store.query.viatra.internal.update; 1package tools.refinery.store.query.viatra.internal.update;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
4import tools.refinery.store.model.Interpretation;
4import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 5import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
5import tools.refinery.store.query.view.TuplePreservingRelationView; 6import tools.refinery.store.query.view.TuplePreservingRelationView;
6import tools.refinery.store.tuple.Tuple; 7import tools.refinery.store.tuple.Tuple;
@@ -9,8 +10,8 @@ public class TuplePreservingRelationViewUpdateListener<T> extends RelationViewUp
9 private final TuplePreservingRelationView<T> view; 10 private final TuplePreservingRelationView<T> view;
10 11
11 TuplePreservingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, 12 TuplePreservingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter,
12 TuplePreservingRelationView<T> view) { 13 TuplePreservingRelationView<T> view, Interpretation<T> interpretation) {
13 super(adapter); 14 super(adapter, interpretation);
14 this.view = view; 15 this.view = view;
15 } 16 }
16 17