aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra
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
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')
-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
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java471
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java607
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java428
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java384
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java52
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java22
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java16
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java19
45 files changed, 3324 insertions, 439 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
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
new file mode 100644
index 00000000..90229b73
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
@@ -0,0 +1,471 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.ModelQuery;
6import tools.refinery.store.query.dnf.Dnf;
7import tools.refinery.store.query.dnf.Query;
8import tools.refinery.store.query.term.Variable;
9import tools.refinery.store.query.viatra.tests.QueryEngineTest;
10import tools.refinery.store.query.view.FunctionalRelationView;
11import tools.refinery.store.query.view.KeyOnlyRelationView;
12import tools.refinery.store.representation.Symbol;
13import tools.refinery.store.tuple.Tuple;
14
15import java.util.Map;
16import java.util.Optional;
17
18import static tools.refinery.store.query.literal.Literals.not;
19import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM;
20import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
21import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
22
23class DiagonalQueryTest {
24 @QueryEngineTest
25 void inputKeyNegationTest(QueryEvaluationHint hint) {
26 var person = new Symbol<>("Person", 1, Boolean.class, false);
27 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
28 var personView = new KeyOnlyRelationView<>(person);
29 var symbolView = new KeyOnlyRelationView<>(symbol);
30
31 var p1 = Variable.of("p1");
32 var p2 = Variable.of("p2");
33 var query = Query.builder("Diagonal")
34 .parameter(p1)
35 .clause(
36 personView.call(p1),
37 not(symbolView.call(p1, p1, p2, p2))
38 )
39 .build();
40
41 var store = ModelStore.builder()
42 .symbols(person, symbol)
43 .with(ViatraModelQuery.ADAPTER)
44 .defaultHint(hint)
45 .queries(query)
46 .build();
47
48 var model = store.createEmptyModel();
49 var personInterpretation = model.getInterpretation(person);
50 var symbolInterpretation = model.getInterpretation(symbol);
51 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
52 var queryResultSet = queryEngine.getResultSet(query);
53
54 personInterpretation.put(Tuple.of(0), true);
55 personInterpretation.put(Tuple.of(1), true);
56 personInterpretation.put(Tuple.of(2), true);
57
58 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
59 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
60 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
61 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
62
63 queryEngine.flushChanges();
64 assertResults(Map.of(
65 Tuple.of(0), false,
66 Tuple.of(1), true,
67 Tuple.of(2), true,
68 Tuple.of(3), false
69 ), queryResultSet);
70 }
71
72 @QueryEngineTest
73 void subQueryNegationTest(QueryEvaluationHint hint) {
74 var person = new Symbol<>("Person", 1, Boolean.class, false);
75 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
76 var personView = new KeyOnlyRelationView<>(person);
77 var symbolView = new KeyOnlyRelationView<>(symbol);
78
79 var p1 = Variable.of("p1");
80 var p2 = Variable.of("p2");
81 var p3 = Variable.of("p3");
82 var p4 = Variable.of("p4");
83 var subQuery = Dnf.builder("SubQuery")
84 .parameters(p1, p2, p3, p4)
85 .clause(
86 personView.call(p1),
87 symbolView.call(p1, p2, p3, p4)
88 )
89 .clause(
90 personView.call(p2),
91 symbolView.call(p1, p2, p3, p4)
92 )
93 .build();
94 var query = Query.builder("Diagonal")
95 .parameter(p1)
96 .clause(
97 personView.call(p1),
98 not(subQuery.call(p1, p1, p2, p2))
99 )
100 .build();
101
102 var store = ModelStore.builder()
103 .symbols(person, symbol)
104 .with(ViatraModelQuery.ADAPTER)
105 .defaultHint(hint)
106 .queries(query)
107 .build();
108
109 var model = store.createEmptyModel();
110 var personInterpretation = model.getInterpretation(person);
111 var symbolInterpretation = model.getInterpretation(symbol);
112 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
113 var queryResultSet = queryEngine.getResultSet(query);
114
115 personInterpretation.put(Tuple.of(0), true);
116 personInterpretation.put(Tuple.of(1), true);
117 personInterpretation.put(Tuple.of(2), true);
118
119 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
120 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
121 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
122 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
123
124 queryEngine.flushChanges();
125 assertResults(Map.of(
126 Tuple.of(0), false,
127 Tuple.of(1), true,
128 Tuple.of(2), true,
129 Tuple.of(3), false
130 ), queryResultSet);
131 }
132
133 @QueryEngineTest
134 void inputKeyCountTest(QueryEvaluationHint hint) {
135 var person = new Symbol<>("Person", 1, Boolean.class, false);
136 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
137 var personView = new KeyOnlyRelationView<>(person);
138 var symbolView = new KeyOnlyRelationView<>(symbol);
139
140 var p1 = Variable.of("p1");
141 var p2 = Variable.of("p2");
142 var x = Variable.of("x", Integer.class);
143 var query = Query.builder("Diagonal")
144 .parameter(p1)
145 .output(x)
146 .clause(
147 personView.call(p1),
148 x.assign(symbolView.count(p1, p1, p2, p2))
149 )
150 .build();
151
152 var store = ModelStore.builder()
153 .symbols(person, symbol)
154 .with(ViatraModelQuery.ADAPTER)
155 .defaultHint(hint)
156 .queries(query)
157 .build();
158
159 var model = store.createEmptyModel();
160 var personInterpretation = model.getInterpretation(person);
161 var symbolInterpretation = model.getInterpretation(symbol);
162 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
163 var queryResultSet = queryEngine.getResultSet(query);
164
165 personInterpretation.put(Tuple.of(0), true);
166 personInterpretation.put(Tuple.of(1), true);
167 personInterpretation.put(Tuple.of(2), true);
168
169 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
170 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
171 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
172 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
173 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
174
175 queryEngine.flushChanges();
176 assertNullableResults(Map.of(
177 Tuple.of(0), Optional.of(2),
178 Tuple.of(1), Optional.of(0),
179 Tuple.of(2), Optional.of(0),
180 Tuple.of(3), Optional.empty()
181 ), queryResultSet);
182 }
183
184 @QueryEngineTest
185 void subQueryCountTest(QueryEvaluationHint hint) {
186 var person = new Symbol<>("Person", 1, Boolean.class, false);
187 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
188 var personView = new KeyOnlyRelationView<>(person);
189 var symbolView = new KeyOnlyRelationView<>(symbol);
190
191 var p1 = Variable.of("p1");
192 var p2 = Variable.of("p2");
193 var p3 = Variable.of("p3");
194 var p4 = Variable.of("p4");
195 var x = Variable.of("x", Integer.class);
196 var subQuery = Dnf.builder("SubQuery")
197 .parameters(p1, p2, p3, p4)
198 .clause(
199 personView.call(p1),
200 symbolView.call(p1, p2, p3, p4)
201 )
202 .clause(
203 personView.call(p2),
204 symbolView.call(p1, p2, p3, p4)
205 )
206 .build();
207 var query = Query.builder("Diagonal")
208 .parameter(p1)
209 .output(x)
210 .clause(
211 personView.call(p1),
212 x.assign(subQuery.count(p1, p1, p2, p2))
213 )
214 .build();
215
216 var store = ModelStore.builder()
217 .symbols(person, symbol)
218 .with(ViatraModelQuery.ADAPTER)
219 .defaultHint(hint)
220 .queries(query)
221 .build();
222
223 var model = store.createEmptyModel();
224 var personInterpretation = model.getInterpretation(person);
225 var symbolInterpretation = model.getInterpretation(symbol);
226 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
227 var queryResultSet = queryEngine.getResultSet(query);
228
229 personInterpretation.put(Tuple.of(0), true);
230 personInterpretation.put(Tuple.of(1), true);
231 personInterpretation.put(Tuple.of(2), true);
232
233 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
234 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
235 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
236 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
237 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
238
239 queryEngine.flushChanges();
240 assertNullableResults(Map.of(
241 Tuple.of(0), Optional.of(2),
242 Tuple.of(1), Optional.of(0),
243 Tuple.of(2), Optional.of(0),
244 Tuple.of(3), Optional.empty()
245 ), queryResultSet);
246 }
247
248 @QueryEngineTest
249 void inputKeyAggregationTest(QueryEvaluationHint hint) {
250 var person = new Symbol<>("Person", 1, Boolean.class, false);
251 var symbol = new Symbol<>("symbol", 4, Integer.class, null);
252 var personView = new KeyOnlyRelationView<>(person);
253 var symbolView = new FunctionalRelationView<>(symbol);
254
255 var p1 = Variable.of("p1");
256 var p2 = Variable.of("p2");
257 var x = Variable.of("x", Integer.class);
258 var y = Variable.of("y", Integer.class);
259 var query = Query.builder("Diagonal")
260 .parameter(p1)
261 .output(x)
262 .clause(
263 personView.call(p1),
264 x.assign(symbolView.aggregate(y, INT_SUM, p1, p1, p2, p2, y))
265 )
266 .build();
267
268 var store = ModelStore.builder()
269 .symbols(person, symbol)
270 .with(ViatraModelQuery.ADAPTER)
271 .defaultHint(hint)
272 .queries(query)
273 .build();
274
275 var model = store.createEmptyModel();
276 var personInterpretation = model.getInterpretation(person);
277 var symbolInterpretation = model.getInterpretation(symbol);
278 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
279 var queryResultSet = queryEngine.getResultSet(query);
280
281 personInterpretation.put(Tuple.of(0), true);
282 personInterpretation.put(Tuple.of(1), true);
283 personInterpretation.put(Tuple.of(2), true);
284
285 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
286 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
287 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
288 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
289 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
290
291 queryEngine.flushChanges();
292 assertNullableResults(Map.of(
293 Tuple.of(0), Optional.of(3),
294 Tuple.of(1), Optional.of(0),
295 Tuple.of(2), Optional.of(0),
296 Tuple.of(3), Optional.empty()
297 ), queryResultSet);
298 }
299
300 @QueryEngineTest
301 void subQueryAggregationTest(QueryEvaluationHint hint) {
302 var person = new Symbol<>("Person", 1, Boolean.class, false);
303 var symbol = new Symbol<>("symbol", 4, Integer.class, null);
304 var personView = new KeyOnlyRelationView<>(person);
305 var symbolView = new FunctionalRelationView<>(symbol);
306
307 var p1 = Variable.of("p1");
308 var p2 = Variable.of("p2");
309 var p3 = Variable.of("p3");
310 var p4 = Variable.of("p4");
311 var x = Variable.of("x", Integer.class);
312 var y = Variable.of("y", Integer.class);
313 var z = Variable.of("z", Integer.class);
314 var subQuery = Dnf.builder("SubQuery")
315 .parameters(p1, p2, p3, p4, x, y)
316 .clause(
317 personView.call(p1),
318 symbolView.call(p1, p2, p3, p4, x),
319 y.assign(x)
320 )
321 .clause(
322 personView.call(p2),
323 symbolView.call(p1, p2, p3, p4, x),
324 y.assign(x)
325 )
326 .build();
327 var query = Query.builder("Diagonal")
328 .parameter(p1)
329 .output(x)
330 .clause(
331 personView.call(p1),
332 x.assign(subQuery.aggregate(z, INT_SUM, p1, p1, p2, p2, z, z))
333 )
334 .build();
335
336 var store = ModelStore.builder()
337 .symbols(person, symbol)
338 .with(ViatraModelQuery.ADAPTER)
339 .defaultHint(hint)
340 .queries(query)
341 .build();
342
343 var model = store.createEmptyModel();
344 var personInterpretation = model.getInterpretation(person);
345 var symbolInterpretation = model.getInterpretation(symbol);
346 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
347 var queryResultSet = queryEngine.getResultSet(query);
348
349 personInterpretation.put(Tuple.of(0), true);
350 personInterpretation.put(Tuple.of(1), true);
351 personInterpretation.put(Tuple.of(2), true);
352
353 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
354 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
355 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
356 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
357 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
358
359 queryEngine.flushChanges();
360 assertNullableResults(Map.of(
361 Tuple.of(0), Optional.of(3),
362 Tuple.of(1), Optional.of(0),
363 Tuple.of(2), Optional.of(0),
364 Tuple.of(3), Optional.empty()
365 ), queryResultSet);
366 }
367
368 @QueryEngineTest
369 void inputKeyTransitiveTest(QueryEvaluationHint hint) {
370 var person = new Symbol<>("Person", 1, Boolean.class, false);
371 var symbol = new Symbol<>("symbol", 2, Boolean.class, false);
372 var personView = new KeyOnlyRelationView<>(person);
373 var symbolView = new KeyOnlyRelationView<>(symbol);
374
375 var p1 = Variable.of("p1");
376 var query = Query.builder("Diagonal")
377 .parameter(p1)
378 .clause(
379 personView.call(p1),
380 symbolView.callTransitive(p1, p1)
381 )
382 .build();
383
384 var store = ModelStore.builder()
385 .symbols(person, symbol)
386 .with(ViatraModelQuery.ADAPTER)
387 .defaultHint(hint)
388 .queries(query)
389 .build();
390
391 var model = store.createEmptyModel();
392 var personInterpretation = model.getInterpretation(person);
393 var symbolInterpretation = model.getInterpretation(symbol);
394 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
395 var queryResultSet = queryEngine.getResultSet(query);
396
397 personInterpretation.put(Tuple.of(0), true);
398 personInterpretation.put(Tuple.of(1), true);
399 personInterpretation.put(Tuple.of(2), true);
400
401 symbolInterpretation.put(Tuple.of(0, 0), true);
402 symbolInterpretation.put(Tuple.of(0, 1), true);
403 symbolInterpretation.put(Tuple.of(1, 2), true);
404
405 queryEngine.flushChanges();
406 assertResults(Map.of(
407 Tuple.of(0), true,
408 Tuple.of(1), false,
409 Tuple.of(2), false,
410 Tuple.of(3), false
411 ), queryResultSet);
412 }
413
414 @QueryEngineTest
415 void subQueryTransitiveTest(QueryEvaluationHint hint) {
416 var person = new Symbol<>("Person", 1, Boolean.class, false);
417 var symbol = new Symbol<>("symbol", 2, Boolean.class, false);
418 var personView = new KeyOnlyRelationView<>(person);
419 var symbolView = new KeyOnlyRelationView<>(symbol);
420
421 var p1 = Variable.of("p1");
422 var p2 = Variable.of("p2");
423 var subQuery = Dnf.builder("SubQuery")
424 .parameters(p1, p2)
425 .clause(
426 personView.call(p1),
427 symbolView.call(p1, p2)
428 )
429 .clause(
430 personView.call(p2),
431 symbolView.call(p1, p2)
432 )
433 .build();
434 var query = Query.builder("Diagonal")
435 .parameter(p1)
436 .clause(
437 personView.call(p1),
438 subQuery.callTransitive(p1, p1)
439 )
440 .build();
441
442 var store = ModelStore.builder()
443 .symbols(person, symbol)
444 .with(ViatraModelQuery.ADAPTER)
445 .defaultHint(hint)
446 .queries(query)
447 .build();
448
449 var model = store.createEmptyModel();
450 var personInterpretation = model.getInterpretation(person);
451 var symbolInterpretation = model.getInterpretation(symbol);
452 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
453 var queryResultSet = queryEngine.getResultSet(query);
454
455 personInterpretation.put(Tuple.of(0), true);
456 personInterpretation.put(Tuple.of(1), true);
457 personInterpretation.put(Tuple.of(2), true);
458
459 symbolInterpretation.put(Tuple.of(0, 0), true);
460 symbolInterpretation.put(Tuple.of(0, 1), true);
461 symbolInterpretation.put(Tuple.of(1, 2), true);
462
463 queryEngine.flushChanges();
464 assertResults(Map.of(
465 Tuple.of(0), true,
466 Tuple.of(1), false,
467 Tuple.of(2), false,
468 Tuple.of(3), false
469 ), queryResultSet);
470 }
471}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
new file mode 100644
index 00000000..fa2a008f
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
@@ -0,0 +1,607 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.query.ModelQuery;
7import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.dnf.Query;
9import tools.refinery.store.query.term.Variable;
10import tools.refinery.store.query.viatra.tests.QueryEngineTest;
11import tools.refinery.store.query.view.FilteredRelationView;
12import tools.refinery.store.query.view.FunctionalRelationView;
13import tools.refinery.store.query.view.KeyOnlyRelationView;
14import tools.refinery.store.representation.Symbol;
15import tools.refinery.store.representation.TruthValue;
16import tools.refinery.store.tuple.Tuple;
17
18import java.util.Map;
19import java.util.Optional;
20
21import static org.hamcrest.MatcherAssert.assertThat;
22import static org.hamcrest.Matchers.is;
23import static org.hamcrest.Matchers.nullValue;
24import static org.junit.jupiter.api.Assertions.assertAll;
25import static org.junit.jupiter.api.Assertions.assertThrows;
26import static tools.refinery.store.query.literal.Literals.assume;
27import static tools.refinery.store.query.term.int_.IntTerms.*;
28import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
29import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
30
31class FunctionalQueryTest {
32 @QueryEngineTest
33 void inputKeyTest(QueryEvaluationHint hint) {
34 var person = new Symbol<>("Person", 1, Boolean.class, false);
35 var age = new Symbol<>("age", 1, Integer.class, null);
36 var personView = new KeyOnlyRelationView<>(person);
37 var ageView = new FunctionalRelationView<>(age);
38
39 var p1 = Variable.of("p1");
40 var x = Variable.of("x", Integer.class);
41 var query = Query.builder("InputKey")
42 .parameter(p1)
43 .output(x)
44 .clause(
45 personView.call(p1),
46 ageView.call(p1, x)
47 )
48 .build();
49
50 var store = ModelStore.builder()
51 .symbols(person, age)
52 .with(ViatraModelQuery.ADAPTER)
53 .defaultHint(hint)
54 .queries(query)
55 .build();
56
57 var model = store.createEmptyModel();
58 var personInterpretation = model.getInterpretation(person);
59 var ageInterpretation = model.getInterpretation(age);
60 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
61 var queryResultSet = queryEngine.getResultSet(query);
62
63 personInterpretation.put(Tuple.of(0), true);
64 personInterpretation.put(Tuple.of(1), true);
65
66 ageInterpretation.put(Tuple.of(0), 12);
67 ageInterpretation.put(Tuple.of(1), 24);
68 ageInterpretation.put(Tuple.of(2), 36);
69
70 queryEngine.flushChanges();
71 assertNullableResults(Map.of(
72 Tuple.of(0), Optional.of(12),
73 Tuple.of(1), Optional.of(24),
74 Tuple.of(2), Optional.empty()
75 ), queryResultSet);
76 }
77
78 @QueryEngineTest
79 void predicateTest(QueryEvaluationHint hint) {
80 var person = new Symbol<>("Person", 1, Boolean.class, false);
81 var age = new Symbol<>("age", 1, Integer.class, null);
82 var personView = new KeyOnlyRelationView<>(person);
83 var ageView = new FunctionalRelationView<>(age);
84
85 var p1 = Variable.of("p1");
86 var x = Variable.of("x", Integer.class);
87 var subQuery = Dnf.builder("SubQuery")
88 .parameters(p1, x)
89 .clause(
90 personView.call(p1),
91 ageView.call(p1, x)
92 )
93 .build();
94 var query = Query.builder("Predicate")
95 .parameter(p1)
96 .output(x)
97 .clause(
98 personView.call(p1),
99 subQuery.call(p1, x)
100 )
101 .build();
102
103 var store = ModelStore.builder()
104 .symbols(person, age)
105 .with(ViatraModelQuery.ADAPTER)
106 .defaultHint(hint)
107 .queries(query)
108 .build();
109
110 var model = store.createEmptyModel();
111 var personInterpretation = model.getInterpretation(person);
112 var ageInterpretation = model.getInterpretation(age);
113 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
114 var queryResultSet = queryEngine.getResultSet(query);
115
116 personInterpretation.put(Tuple.of(0), true);
117 personInterpretation.put(Tuple.of(1), true);
118
119 ageInterpretation.put(Tuple.of(0), 12);
120 ageInterpretation.put(Tuple.of(1), 24);
121 ageInterpretation.put(Tuple.of(2), 36);
122
123 queryEngine.flushChanges();
124 assertNullableResults(Map.of(
125 Tuple.of(0), Optional.of(12),
126 Tuple.of(1), Optional.of(24),
127 Tuple.of(2), Optional.empty()
128 ), queryResultSet);
129 }
130
131 @QueryEngineTest
132 void computationTest(QueryEvaluationHint hint) {
133 var person = new Symbol<>("Person", 1, Boolean.class, false);
134 var age = new Symbol<>("age", 1, Integer.class, null);
135 var personView = new KeyOnlyRelationView<>(person);
136 var ageView = new FunctionalRelationView<>(age);
137
138 var p1 = Variable.of("p1");
139 var x = Variable.of("x", Integer.class);
140 var y = Variable.of("y", Integer.class);
141 var query = Query.builder("Computation")
142 .parameter(p1)
143 .output(y)
144 .clause(
145 personView.call(p1),
146 ageView.call(p1, x),
147 y.assign(mul(x, constant(7)))
148 )
149 .build();
150
151 var store = ModelStore.builder()
152 .symbols(person, age)
153 .with(ViatraModelQuery.ADAPTER)
154 .defaultHint(hint)
155 .queries(query)
156 .build();
157
158 var model = store.createEmptyModel();
159 var personInterpretation = model.getInterpretation(person);
160 var ageInterpretation = model.getInterpretation(age);
161 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
162 var queryResultSet = queryEngine.getResultSet(query);
163
164 personInterpretation.put(Tuple.of(0), true);
165 personInterpretation.put(Tuple.of(1), true);
166
167 ageInterpretation.put(Tuple.of(0), 12);
168 ageInterpretation.put(Tuple.of(1), 24);
169
170 queryEngine.flushChanges();
171 assertNullableResults(Map.of(
172 Tuple.of(0), Optional.of(84),
173 Tuple.of(1), Optional.of(168),
174 Tuple.of(2), Optional.empty()
175 ), queryResultSet);
176 }
177
178 @QueryEngineTest
179 void inputKeyCountTest(QueryEvaluationHint hint) {
180 var person = new Symbol<>("Person", 1, Boolean.class, false);
181 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
182 var personView = new KeyOnlyRelationView<>(person);
183 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
184
185 var p1 = Variable.of("p1");
186 var p2 = Variable.of("p2");
187 var x = Variable.of("x", Integer.class);
188 var query = Query.builder("Count")
189 .parameter(p1)
190 .output(x)
191 .clause(
192 personView.call(p1),
193 x.assign(friendMustView.count(p1, p2))
194 )
195 .build();
196
197 var store = ModelStore.builder()
198 .symbols(person, friend)
199 .with(ViatraModelQuery.ADAPTER)
200 .defaultHint(hint)
201 .queries(query)
202 .build();
203
204 var model = store.createEmptyModel();
205 var personInterpretation = model.getInterpretation(person);
206 var friendInterpretation = model.getInterpretation(friend);
207 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
208 var queryResultSet = queryEngine.getResultSet(query);
209
210 personInterpretation.put(Tuple.of(0), true);
211 personInterpretation.put(Tuple.of(1), true);
212 personInterpretation.put(Tuple.of(2), true);
213
214 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
215 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
216 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
217
218 queryEngine.flushChanges();
219 assertNullableResults(Map.of(
220 Tuple.of(0), Optional.of(1),
221 Tuple.of(1), Optional.of(2),
222 Tuple.of(2), Optional.of(0),
223 Tuple.of(3), Optional.empty()
224 ), queryResultSet);
225 }
226
227 @QueryEngineTest
228 void predicateCountTest(QueryEvaluationHint hint) {
229 var person = new Symbol<>("Person", 1, Boolean.class, false);
230 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
231 var personView = new KeyOnlyRelationView<>(person);
232 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
233
234 var p1 = Variable.of("p1");
235 var p2 = Variable.of("p2");
236 var x = Variable.of("x", Integer.class);
237 var subQuery = Dnf.builder("SubQuery")
238 .parameters(p1, p2)
239 .clause(
240 personView.call(p1),
241 personView.call(p2),
242 friendMustView.call(p1, p2)
243 )
244 .build();
245 var query = Query.builder("Count")
246 .parameter(p1)
247 .output(x)
248 .clause(
249 personView.call(p1),
250 x.assign(subQuery.count(p1, p2))
251 )
252 .build();
253
254 var store = ModelStore.builder()
255 .symbols(person, friend)
256 .with(ViatraModelQuery.ADAPTER)
257 .defaultHint(hint)
258 .queries(query)
259 .build();
260
261 var model = store.createEmptyModel();
262 var personInterpretation = model.getInterpretation(person);
263 var friendInterpretation = model.getInterpretation(friend);
264 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
265 var queryResultSet = queryEngine.getResultSet(query);
266
267 personInterpretation.put(Tuple.of(0), true);
268 personInterpretation.put(Tuple.of(1), true);
269 personInterpretation.put(Tuple.of(2), true);
270
271 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
272 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
273 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
274
275 queryEngine.flushChanges();
276 assertNullableResults(Map.of(
277 Tuple.of(0), Optional.of(1),
278 Tuple.of(1), Optional.of(2),
279 Tuple.of(2), Optional.of(0),
280 Tuple.of(3), Optional.empty()
281 ), queryResultSet);
282 }
283
284 @QueryEngineTest
285 void inputKeyAggregationTest(QueryEvaluationHint hint) {
286 var age = new Symbol<>("age", 1, Integer.class, null);
287 var ageView = new FunctionalRelationView<>(age);
288
289 var p1 = Variable.of("p1");
290 var x = Variable.of("x", Integer.class);
291 var y = Variable.of("y", Integer.class);
292 var query = Query.builder("Aggregate")
293 .output(x)
294 .clause(
295 x.assign(ageView.aggregate(y, INT_SUM, p1, y))
296 )
297 .build();
298
299 var store = ModelStore.builder()
300 .symbols(age)
301 .with(ViatraModelQuery.ADAPTER)
302 .defaultHint(hint)
303 .queries(query)
304 .build();
305
306 var model = store.createEmptyModel();
307 var ageInterpretation = model.getInterpretation(age);
308 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
309 var queryResultSet = queryEngine.getResultSet(query);
310
311 ageInterpretation.put(Tuple.of(0), 12);
312 ageInterpretation.put(Tuple.of(1), 24);
313
314 queryEngine.flushChanges();
315 assertResults(Map.of(Tuple.of(), 36), queryResultSet);
316 }
317
318 @QueryEngineTest
319 void predicateAggregationTest(QueryEvaluationHint hint) {
320 var person = new Symbol<>("Person", 1, Boolean.class, false);
321 var age = new Symbol<>("age", 1, Integer.class, null);
322 var personView = new KeyOnlyRelationView<>(person);
323 var ageView = new FunctionalRelationView<>(age);
324
325 var p1 = Variable.of("p1");
326 var x = Variable.of("x", Integer.class);
327 var y = Variable.of("y", Integer.class);
328 var subQuery = Dnf.builder("SubQuery")
329 .parameters(p1, x)
330 .clause(
331 personView.call(p1),
332 ageView.call(p1, x)
333 )
334 .build();
335 var query = Query.builder("Aggregate")
336 .output(x)
337 .clause(
338 x.assign(subQuery.aggregate(y, INT_SUM, p1, y))
339 )
340 .build();
341
342 var store = ModelStore.builder()
343 .symbols(person, age)
344 .with(ViatraModelQuery.ADAPTER)
345 .defaultHint(hint)
346 .queries(query)
347 .build();
348
349 var model = store.createEmptyModel();
350 var personInterpretation = model.getInterpretation(person);
351 var ageInterpretation = model.getInterpretation(age);
352 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
353 var queryResultSet = queryEngine.getResultSet(query);
354
355 personInterpretation.put(Tuple.of(0), true);
356 personInterpretation.put(Tuple.of(1), true);
357
358 ageInterpretation.put(Tuple.of(0), 12);
359 ageInterpretation.put(Tuple.of(1), 24);
360
361 queryEngine.flushChanges();
362 assertResults(Map.of(Tuple.of(), 36), queryResultSet);
363 }
364
365 @QueryEngineTest
366 void extremeValueTest(QueryEvaluationHint hint) {
367 var person = new Symbol<>("Person", 1, Boolean.class, false);
368 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
369 var personView = new KeyOnlyRelationView<>(person);
370 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
371
372 var p1 = Variable.of("p1");
373 var p2 = Variable.of("p2");
374 var x = Variable.of("x", Integer.class);
375 var y = Variable.of("y", Integer.class);
376 var subQuery = Dnf.builder("SubQuery")
377 .parameters(p1, x)
378 .clause(
379 personView.call(p1),
380 x.assign(friendMustView.count(p1, p2))
381 )
382 .build();
383 var minQuery = Query.builder("Min")
384 .output(x)
385 .clause(
386 x.assign(subQuery.aggregate(y, INT_MIN, p1, y))
387 )
388 .build();
389 var maxQuery = Query.builder("Max")
390 .output(x)
391 .clause(
392 x.assign(subQuery.aggregate(y, INT_MAX, p1, y))
393 )
394 .build();
395
396 var store = ModelStore.builder()
397 .symbols(person, friend)
398 .with(ViatraModelQuery.ADAPTER)
399 .defaultHint(hint)
400 .queries(minQuery, maxQuery)
401 .build();
402
403 var model = store.createEmptyModel();
404 var personInterpretation = model.getInterpretation(person);
405 var friendInterpretation = model.getInterpretation(friend);
406 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
407 var minResultSet = queryEngine.getResultSet(minQuery);
408 var maxResultSet = queryEngine.getResultSet(maxQuery);
409
410 assertResults(Map.of(Tuple.of(), Integer.MAX_VALUE), minResultSet);
411 assertResults(Map.of(Tuple.of(), Integer.MIN_VALUE), maxResultSet);
412
413 personInterpretation.put(Tuple.of(0), true);
414 personInterpretation.put(Tuple.of(1), true);
415 personInterpretation.put(Tuple.of(2), true);
416
417 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
418 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
419 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
420
421 queryEngine.flushChanges();
422 assertResults(Map.of(Tuple.of(), 0), minResultSet);
423 assertResults(Map.of(Tuple.of(), 2), maxResultSet);
424
425 friendInterpretation.put(Tuple.of(2, 0), TruthValue.TRUE);
426 friendInterpretation.put(Tuple.of(2, 1), TruthValue.TRUE);
427
428 queryEngine.flushChanges();
429 assertResults(Map.of(Tuple.of(), 1), minResultSet);
430 assertResults(Map.of(Tuple.of(), 2), maxResultSet);
431
432 friendInterpretation.put(Tuple.of(0, 1), TruthValue.FALSE);
433 friendInterpretation.put(Tuple.of(1, 0), TruthValue.FALSE);
434 friendInterpretation.put(Tuple.of(2, 0), TruthValue.FALSE);
435
436 queryEngine.flushChanges();
437 assertResults(Map.of(Tuple.of(), 0), minResultSet);
438 assertResults(Map.of(Tuple.of(), 1), maxResultSet);
439 }
440
441 @QueryEngineTest
442 void invalidComputationTest(QueryEvaluationHint hint) {
443 var person = new Symbol<>("Person", 1, Boolean.class, false);
444 var age = new Symbol<>("age", 1, Integer.class, null);
445 var personView = new KeyOnlyRelationView<>(person);
446 var ageView = new FunctionalRelationView<>(age);
447
448 var p1 = Variable.of("p1");
449 var x = Variable.of("x", Integer.class);
450 var y = Variable.of("y", Integer.class);
451 var query = Query.builder("InvalidComputation")
452 .parameter(p1)
453 .output(y)
454 .clause(
455 personView.call(p1),
456 ageView.call(p1, x),
457 y.assign(div(constant(120), x))
458 )
459 .build();
460
461 var store = ModelStore.builder()
462 .symbols(person, age)
463 .with(ViatraModelQuery.ADAPTER)
464 .defaultHint(hint)
465 .queries(query)
466 .build();
467
468 var model = store.createEmptyModel();
469 var personInterpretation = model.getInterpretation(person);
470 var ageInterpretation = model.getInterpretation(age);
471 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
472 var queryResultSet = queryEngine.getResultSet(query);
473
474 personInterpretation.put(Tuple.of(0), true);
475 personInterpretation.put(Tuple.of(1), true);
476
477 ageInterpretation.put(Tuple.of(0), 0);
478 ageInterpretation.put(Tuple.of(1), 30);
479
480 queryEngine.flushChanges();
481 assertNullableResults(Map.of(
482 Tuple.of(0), Optional.empty(),
483 Tuple.of(1), Optional.of(4),
484 Tuple.of(2), Optional.empty()
485 ), queryResultSet);
486 }
487
488 @QueryEngineTest
489 void invalidAssumeTest(QueryEvaluationHint hint) {
490 var person = new Symbol<>("Person", 1, Boolean.class, false);
491 var age = new Symbol<>("age", 1, Integer.class, null);
492 var personView = new KeyOnlyRelationView<>(person);
493 var ageView = new FunctionalRelationView<>(age);
494
495 var p1 = Variable.of("p1");
496 var x = Variable.of("x", Integer.class);
497 var query = Query.builder("InvalidComputation")
498 .parameter(p1)
499 .clause(
500 personView.call(p1),
501 ageView.call(p1, x),
502 assume(lessEq(div(constant(120), x), constant(5)))
503 )
504 .build();
505
506 var store = ModelStore.builder()
507 .symbols(person, age)
508 .with(ViatraModelQuery.ADAPTER)
509 .defaultHint(hint)
510 .queries(query)
511 .build();
512
513 var model = store.createEmptyModel();
514 var personInterpretation = model.getInterpretation(person);
515 var ageInterpretation = model.getInterpretation(age);
516 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
517 var queryResultSet = queryEngine.getResultSet(query);
518
519 personInterpretation.put(Tuple.of(0), true);
520 personInterpretation.put(Tuple.of(1), true);
521 personInterpretation.put(Tuple.of(2), true);
522
523 ageInterpretation.put(Tuple.of(0), 0);
524 ageInterpretation.put(Tuple.of(1), 30);
525 ageInterpretation.put(Tuple.of(2), 20);
526
527 queryEngine.flushChanges();
528 assertResults(Map.of(
529 Tuple.of(0), false,
530 Tuple.of(1), true,
531 Tuple.of(2), false,
532 Tuple.of(3), false
533 ), queryResultSet);
534 }
535
536 @QueryEngineTest
537 void notFunctionalTest(QueryEvaluationHint hint) {
538 var person = new Symbol<>("Person", 1, Boolean.class, false);
539 var age = new Symbol<>("age", 1, Integer.class, null);
540 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
541 var personView = new KeyOnlyRelationView<>(person);
542 var ageView = new FunctionalRelationView<>(age);
543 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
544
545 var p1 = Variable.of("p1");
546 var p2 = Variable.of("p2");
547 var x = Variable.of("x", Integer.class);
548 var query = Query.builder("NotFunctional")
549 .parameter(p1)
550 .output(x)
551 .clause(
552 personView.call(p1),
553 friendMustView.call(p1, p2),
554 ageView.call(p2, x)
555 )
556 .build();
557
558 var store = ModelStore.builder()
559 .symbols(person, age, friend)
560 .with(ViatraModelQuery.ADAPTER)
561 .defaultHint(hint)
562 .query(query)
563 .build();
564
565 var model = store.createEmptyModel();
566 var personInterpretation = model.getInterpretation(person);
567 var ageInterpretation = model.getInterpretation(age);
568 var friendInterpretation = model.getInterpretation(friend);
569 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
570 var queryResultSet = queryEngine.getResultSet(query);
571
572 personInterpretation.put(Tuple.of(0), true);
573 personInterpretation.put(Tuple.of(1), true);
574 personInterpretation.put(Tuple.of(2), true);
575
576 ageInterpretation.put(Tuple.of(0), 24);
577 ageInterpretation.put(Tuple.of(1), 30);
578 ageInterpretation.put(Tuple.of(2), 36);
579
580 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
581 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
582 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
583
584 queryEngine.flushChanges();
585 var invalidTuple = Tuple.of(1);
586 var cursor = queryResultSet.getAll();
587 assertAll(
588 () -> assertThat("value for key 0", queryResultSet.get(Tuple.of(0)), is(30)),
589 () -> assertThrows(IllegalStateException.class, () -> queryResultSet.get(invalidTuple),
590 "multiple values for key 1"),
591 () -> assertThat("value for key 2", queryResultSet.get(Tuple.of(2)), is(nullValue())),
592 () -> assertThat("value for key 3", queryResultSet.get(Tuple.of(3)), is(nullValue()))
593 );
594 if (hint.getQueryBackendRequirementType() != QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH) {
595 // Local search doesn't support throwing an error on multiple function return values.
596 assertThat("results size", queryResultSet.size(), is(2));
597 assertThrows(IllegalStateException.class, () -> enumerateValues(cursor), "move cursor");
598 }
599 }
600
601 private static void enumerateValues(Cursor<?, ?> cursor) {
602 //noinspection StatementWithEmptyBody
603 while (cursor.move()) {
604 // Nothing do, just let the cursor move through the result set.
605 }
606 }
607}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
index 8b25419d..8a3f9d88 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
@@ -1,33 +1,38 @@
1package tools.refinery.store.query.viatra; 1package tools.refinery.store.query.viatra;
2 2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
3import org.junit.jupiter.api.Test; 4import org.junit.jupiter.api.Test;
4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.model.ModelStore; 5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.query.Dnf;
7import tools.refinery.store.query.ModelQuery; 6import tools.refinery.store.query.ModelQuery;
8import tools.refinery.store.query.Variable; 7import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.dnf.Query;
9import tools.refinery.store.query.term.Variable;
10import tools.refinery.store.query.viatra.tests.QueryEngineTest;
9import tools.refinery.store.query.view.FilteredRelationView; 11import tools.refinery.store.query.view.FilteredRelationView;
12import tools.refinery.store.query.view.FunctionalRelationView;
10import tools.refinery.store.query.view.KeyOnlyRelationView; 13import tools.refinery.store.query.view.KeyOnlyRelationView;
11import tools.refinery.store.representation.Symbol; 14import tools.refinery.store.representation.Symbol;
12import tools.refinery.store.representation.TruthValue; 15import tools.refinery.store.representation.TruthValue;
13import tools.refinery.store.tuple.Tuple; 16import tools.refinery.store.tuple.Tuple;
14import tools.refinery.store.tuple.TupleLike;
15 17
16import java.util.HashSet; 18import java.util.Map;
17import java.util.Set;
18 19
19import static org.junit.jupiter.api.Assertions.*; 20import static org.junit.jupiter.api.Assertions.assertThrows;
21import static tools.refinery.store.query.literal.Literals.assume;
20import static tools.refinery.store.query.literal.Literals.not; 22import static tools.refinery.store.query.literal.Literals.not;
23import static tools.refinery.store.query.term.int_.IntTerms.constant;
24import static tools.refinery.store.query.term.int_.IntTerms.greaterEq;
25import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
21 26
22class QueryTest { 27class QueryTest {
23 @Test 28 @QueryEngineTest
24 void typeConstraintTest() { 29 void typeConstraintTest(QueryEvaluationHint hint) {
25 var person = new Symbol<>("Person", 1, Boolean.class, false); 30 var person = new Symbol<>("Person", 1, Boolean.class, false);
26 var asset = new Symbol<>("Asset", 1, Boolean.class, false); 31 var asset = new Symbol<>("Asset", 1, Boolean.class, false);
27 var personView = new KeyOnlyRelationView<>(person); 32 var personView = new KeyOnlyRelationView<>(person);
28 33
29 var p1 = new Variable("p1"); 34 var p1 = Variable.of("p1");
30 var predicate = Dnf.builder("TypeConstraint") 35 var predicate = Query.builder("TypeConstraint")
31 .parameters(p1) 36 .parameters(p1)
32 .clause(personView.call(p1)) 37 .clause(personView.call(p1))
33 .build(); 38 .build();
@@ -35,7 +40,8 @@ class QueryTest {
35 var store = ModelStore.builder() 40 var store = ModelStore.builder()
36 .symbols(person, asset) 41 .symbols(person, asset)
37 .with(ViatraModelQuery.ADAPTER) 42 .with(ViatraModelQuery.ADAPTER)
38 .queries(predicate) 43 .defaultHint(hint)
44 .query(predicate)
39 .build(); 45 .build();
40 46
41 var model = store.createEmptyModel(); 47 var model = store.createEmptyModel();
@@ -51,20 +57,23 @@ class QueryTest {
51 assetInterpretation.put(Tuple.of(2), true); 57 assetInterpretation.put(Tuple.of(2), true);
52 58
53 queryEngine.flushChanges(); 59 queryEngine.flushChanges();
54 assertEquals(2, predicateResultSet.countResults()); 60 assertResults(Map.of(
55 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0), Tuple.of(1))); 61 Tuple.of(0), true,
62 Tuple.of(1), true,
63 Tuple.of(2), false
64 ), predicateResultSet);
56 } 65 }
57 66
58 @Test 67 @QueryEngineTest
59 void relationConstraintTest() { 68 void relationConstraintTest(QueryEvaluationHint hint) {
60 var person = new Symbol<>("Person", 1, Boolean.class, false); 69 var person = new Symbol<>("Person", 1, Boolean.class, false);
61 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 70 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
62 var personView = new KeyOnlyRelationView<>(person); 71 var personView = new KeyOnlyRelationView<>(person);
63 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 72 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
64 73
65 var p1 = new Variable("p1"); 74 var p1 = Variable.of("p1");
66 var p2 = new Variable("p2"); 75 var p2 = Variable.of("p2");
67 var predicate = Dnf.builder("RelationConstraint") 76 var predicate = Query.builder("RelationConstraint")
68 .parameters(p1, p2) 77 .parameters(p1, p2)
69 .clause( 78 .clause(
70 personView.call(p1), 79 personView.call(p1),
@@ -76,6 +85,7 @@ class QueryTest {
76 var store = ModelStore.builder() 85 var store = ModelStore.builder()
77 .symbols(person, friend) 86 .symbols(person, friend)
78 .with(ViatraModelQuery.ADAPTER) 87 .with(ViatraModelQuery.ADAPTER)
88 .defaultHint(hint)
79 .queries(predicate) 89 .queries(predicate)
80 .build(); 90 .build();
81 91
@@ -85,8 +95,6 @@ class QueryTest {
85 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 95 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
86 var predicateResultSet = queryEngine.getResultSet(predicate); 96 var predicateResultSet = queryEngine.getResultSet(predicate);
87 97
88 assertEquals(0, predicateResultSet.countResults());
89
90 personInterpretation.put(Tuple.of(0), true); 98 personInterpretation.put(Tuple.of(0), true);
91 personInterpretation.put(Tuple.of(1), true); 99 personInterpretation.put(Tuple.of(1), true);
92 personInterpretation.put(Tuple.of(2), true); 100 personInterpretation.put(Tuple.of(2), true);
@@ -94,83 +102,27 @@ class QueryTest {
94 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE); 102 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
95 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE); 103 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
96 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 104 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
97 105 friendInterpretation.put(Tuple.of(1, 3), TruthValue.TRUE);
98 assertEquals(0, predicateResultSet.countResults());
99 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 1)));
100 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2)));
101 106
102 queryEngine.flushChanges(); 107 queryEngine.flushChanges();
103 assertEquals(3, predicateResultSet.countResults()); 108 assertResults(Map.of(
104 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(1, 2))); 109 Tuple.of(0, 1), true,
105 assertTrue(predicateResultSet.hasResult(Tuple.of(0, 1))); 110 Tuple.of(1, 0), true,
106 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2))); 111 Tuple.of(1, 2), true,
112 Tuple.of(2, 1), false
113 ), predicateResultSet);
107 } 114 }
108 115
109 @Test 116 @QueryEngineTest
110 void andTest() { 117 void existTest(QueryEvaluationHint hint) {
111 var person = new Symbol<>("Person", 1, Boolean.class, false); 118 var person = new Symbol<>("Person", 1, Boolean.class, false);
112 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 119 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
113 var personView = new KeyOnlyRelationView<>(person); 120 var personView = new KeyOnlyRelationView<>(person);
114 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 121 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
115 122
116 var p1 = new Variable("p1"); 123 var p1 = Variable.of("p1");
117 var p2 = new Variable("p2"); 124 var p2 = Variable.of("p2");
118 var predicate = Dnf.builder("RelationConstraint") 125 var predicate = Query.builder("RelationConstraint")
119 .parameters(p1, p2)
120 .clause(
121 personView.call(p1),
122 personView.call(p2),
123 friendMustView.call(p1, p2),
124 friendMustView.call(p2, p1)
125 )
126 .build();
127
128 var store = ModelStore.builder()
129 .symbols(person, friend)
130 .with(ViatraModelQuery.ADAPTER)
131 .queries(predicate)
132 .build();
133
134 var model = store.createEmptyModel();
135 var personInterpretation = model.getInterpretation(person);
136 var friendInterpretation = model.getInterpretation(friend);
137 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
138 var predicateResultSet = queryEngine.getResultSet(predicate);
139
140 assertEquals(0, predicateResultSet.countResults());
141
142 personInterpretation.put(Tuple.of(0), true);
143 personInterpretation.put(Tuple.of(1), true);
144 personInterpretation.put(Tuple.of(2), true);
145
146 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
147 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
148
149 queryEngine.flushChanges();
150 assertEquals(0, predicateResultSet.countResults());
151
152 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
153 queryEngine.flushChanges();
154 assertEquals(2, predicateResultSet.countResults());
155 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0)));
156
157 friendInterpretation.put(Tuple.of(2, 0), TruthValue.TRUE);
158 queryEngine.flushChanges();
159 assertEquals(4, predicateResultSet.countResults());
160 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(0, 2),
161 Tuple.of(2, 0)));
162 }
163
164 @Test
165 void existTest() {
166 var person = new Symbol<>("Person", 1, Boolean.class, false);
167 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
168 var personView = new KeyOnlyRelationView<>(person);
169 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
170
171 var p1 = new Variable("p1");
172 var p2 = new Variable("p2");
173 var predicate = Dnf.builder("RelationConstraint")
174 .parameters(p1) 126 .parameters(p1)
175 .clause( 127 .clause(
176 personView.call(p1), 128 personView.call(p1),
@@ -182,6 +134,7 @@ class QueryTest {
182 var store = ModelStore.builder() 134 var store = ModelStore.builder()
183 .symbols(person, friend) 135 .symbols(person, friend)
184 .with(ViatraModelQuery.ADAPTER) 136 .with(ViatraModelQuery.ADAPTER)
137 .defaultHint(hint)
185 .queries(predicate) 138 .queries(predicate)
186 .build(); 139 .build();
187 140
@@ -198,16 +151,19 @@ class QueryTest {
198 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE); 151 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
199 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE); 152 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
200 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 153 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
201 154 friendInterpretation.put(Tuple.of(3, 2), TruthValue.TRUE);
202 assertEquals(0, predicateResultSet.countResults());
203 155
204 queryEngine.flushChanges(); 156 queryEngine.flushChanges();
205 assertEquals(2, predicateResultSet.countResults()); 157 assertResults(Map.of(
206 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0), Tuple.of(1))); 158 Tuple.of(0), true,
159 Tuple.of(1), true,
160 Tuple.of(2), false,
161 Tuple.of(3), false
162 ), predicateResultSet);
207 } 163 }
208 164
209 @Test 165 @QueryEngineTest
210 void orTest() { 166 void orTest(QueryEvaluationHint hint) {
211 var person = new Symbol<>("Person", 1, Boolean.class, false); 167 var person = new Symbol<>("Person", 1, Boolean.class, false);
212 var animal = new Symbol<>("Animal", 1, Boolean.class, false); 168 var animal = new Symbol<>("Animal", 1, Boolean.class, false);
213 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 169 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
@@ -215,9 +171,9 @@ class QueryTest {
215 var animalView = new KeyOnlyRelationView<>(animal); 171 var animalView = new KeyOnlyRelationView<>(animal);
216 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 172 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
217 173
218 var p1 = new Variable("p1"); 174 var p1 = Variable.of("p1");
219 var p2 = new Variable("p2"); 175 var p2 = Variable.of("p2");
220 var predicate = Dnf.builder("Or") 176 var predicate = Query.builder("Or")
221 .parameters(p1, p2) 177 .parameters(p1, p2)
222 .clause( 178 .clause(
223 personView.call(p1), 179 personView.call(p1),
@@ -234,6 +190,7 @@ class QueryTest {
234 var store = ModelStore.builder() 190 var store = ModelStore.builder()
235 .symbols(person, animal, friend) 191 .symbols(person, animal, friend)
236 .with(ViatraModelQuery.ADAPTER) 192 .with(ViatraModelQuery.ADAPTER)
193 .defaultHint(hint)
237 .queries(predicate) 194 .queries(predicate)
238 .build(); 195 .build();
239 196
@@ -256,18 +213,23 @@ class QueryTest {
256 friendInterpretation.put(Tuple.of(3, 0), TruthValue.TRUE); 213 friendInterpretation.put(Tuple.of(3, 0), TruthValue.TRUE);
257 214
258 queryEngine.flushChanges(); 215 queryEngine.flushChanges();
259 assertEquals(2, predicateResultSet.countResults()); 216 assertResults(Map.of(
260 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(2, 3))); 217 Tuple.of(0, 1), true,
218 Tuple.of(0, 2), false,
219 Tuple.of(2, 3), true,
220 Tuple.of(3, 0), false,
221 Tuple.of(3, 2), false
222 ), predicateResultSet);
261 } 223 }
262 224
263 @Test 225 @QueryEngineTest
264 void equalityTest() { 226 void equalityTest(QueryEvaluationHint hint) {
265 var person = new Symbol<>("Person", 1, Boolean.class, false); 227 var person = new Symbol<>("Person", 1, Boolean.class, false);
266 var personView = new KeyOnlyRelationView<>(person); 228 var personView = new KeyOnlyRelationView<>(person);
267 229
268 var p1 = new Variable("p1"); 230 var p1 = Variable.of("p1");
269 var p2 = new Variable("p2"); 231 var p2 = Variable.of("p2");
270 var predicate = Dnf.builder("Equality") 232 var predicate = Query.builder("Equality")
271 .parameters(p1, p2) 233 .parameters(p1, p2)
272 .clause( 234 .clause(
273 personView.call(p1), 235 personView.call(p1),
@@ -279,6 +241,7 @@ class QueryTest {
279 var store = ModelStore.builder() 241 var store = ModelStore.builder()
280 .symbols(person) 242 .symbols(person)
281 .with(ViatraModelQuery.ADAPTER) 243 .with(ViatraModelQuery.ADAPTER)
244 .defaultHint(hint)
282 .queries(predicate) 245 .queries(predicate)
283 .build(); 246 .build();
284 247
@@ -292,21 +255,26 @@ class QueryTest {
292 personInterpretation.put(Tuple.of(2), true); 255 personInterpretation.put(Tuple.of(2), true);
293 256
294 queryEngine.flushChanges(); 257 queryEngine.flushChanges();
295 assertEquals(3, predicateResultSet.countResults()); 258 assertResults(Map.of(
296 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 0), Tuple.of(1, 1), Tuple.of(2, 2))); 259 Tuple.of(0, 0), true,
260 Tuple.of(1, 1), true,
261 Tuple.of(2, 2), true,
262 Tuple.of(0, 1), false,
263 Tuple.of(3, 3), false
264 ), predicateResultSet);
297 } 265 }
298 266
299 @Test 267 @QueryEngineTest
300 void inequalityTest() { 268 void inequalityTest(QueryEvaluationHint hint) {
301 var person = new Symbol<>("Person", 1, Boolean.class, false); 269 var person = new Symbol<>("Person", 1, Boolean.class, false);
302 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 270 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
303 var personView = new KeyOnlyRelationView<>(person); 271 var personView = new KeyOnlyRelationView<>(person);
304 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 272 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
305 273
306 var p1 = new Variable("p1"); 274 var p1 = Variable.of("p1");
307 var p2 = new Variable("p2"); 275 var p2 = Variable.of("p2");
308 var p3 = new Variable("p3"); 276 var p3 = Variable.of("p3");
309 var predicate = Dnf.builder("Inequality") 277 var predicate = Query.builder("Inequality")
310 .parameters(p1, p2, p3) 278 .parameters(p1, p2, p3)
311 .clause( 279 .clause(
312 personView.call(p1), 280 personView.call(p1),
@@ -320,6 +288,7 @@ class QueryTest {
320 var store = ModelStore.builder() 288 var store = ModelStore.builder()
321 .symbols(person, friend) 289 .symbols(person, friend)
322 .with(ViatraModelQuery.ADAPTER) 290 .with(ViatraModelQuery.ADAPTER)
291 .defaultHint(hint)
323 .queries(predicate) 292 .queries(predicate)
324 .build(); 293 .build();
325 294
@@ -337,19 +306,22 @@ class QueryTest {
337 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 306 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
338 307
339 queryEngine.flushChanges(); 308 queryEngine.flushChanges();
340 assertEquals(2, predicateResultSet.countResults()); 309 assertResults(Map.of(
341 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1, 2), Tuple.of(1, 0, 2))); 310 Tuple.of(0, 1, 2), true,
311 Tuple.of(1, 0, 2), true,
312 Tuple.of(0, 0, 2), false
313 ), predicateResultSet);
342 } 314 }
343 315
344 @Test 316 @QueryEngineTest
345 void patternCallTest() { 317 void patternCallTest(QueryEvaluationHint hint) {
346 var person = new Symbol<>("Person", 1, Boolean.class, false); 318 var person = new Symbol<>("Person", 1, Boolean.class, false);
347 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 319 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
348 var personView = new KeyOnlyRelationView<>(person); 320 var personView = new KeyOnlyRelationView<>(person);
349 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 321 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
350 322
351 var p1 = new Variable("p1"); 323 var p1 = Variable.of("p1");
352 var p2 = new Variable("p2"); 324 var p2 = Variable.of("p2");
353 var friendPredicate = Dnf.builder("RelationConstraint") 325 var friendPredicate = Dnf.builder("RelationConstraint")
354 .parameters(p1, p2) 326 .parameters(p1, p2)
355 .clause( 327 .clause(
@@ -359,9 +331,9 @@ class QueryTest {
359 ) 331 )
360 .build(); 332 .build();
361 333
362 var p3 = new Variable("p3"); 334 var p3 = Variable.of("p3");
363 var p4 = new Variable("p4"); 335 var p4 = Variable.of("p4");
364 var predicate = Dnf.builder("PositivePatternCall") 336 var predicate = Query.builder("PositivePatternCall")
365 .parameters(p3, p4) 337 .parameters(p3, p4)
366 .clause( 338 .clause(
367 personView.call(p3), 339 personView.call(p3),
@@ -373,6 +345,7 @@ class QueryTest {
373 var store = ModelStore.builder() 345 var store = ModelStore.builder()
374 .symbols(person, friend) 346 .symbols(person, friend)
375 .with(ViatraModelQuery.ADAPTER) 347 .with(ViatraModelQuery.ADAPTER)
348 .defaultHint(hint)
376 .queries(predicate) 349 .queries(predicate)
377 .build(); 350 .build();
378 351
@@ -391,19 +364,24 @@ class QueryTest {
391 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 364 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
392 365
393 queryEngine.flushChanges(); 366 queryEngine.flushChanges();
394 assertEquals(3, predicateResultSet.countResults()); 367 assertResults(Map.of(
368 Tuple.of(0, 1), true,
369 Tuple.of(1, 0), true,
370 Tuple.of(1, 2), true,
371 Tuple.of(2, 1), false
372 ), predicateResultSet);
395 } 373 }
396 374
397 @Test 375 @QueryEngineTest
398 void negativeRelationViewTest() { 376 void negativeRelationViewTest(QueryEvaluationHint hint) {
399 var person = new Symbol<>("Person", 1, Boolean.class, false); 377 var person = new Symbol<>("Person", 1, Boolean.class, false);
400 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 378 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
401 var personView = new KeyOnlyRelationView<>(person); 379 var personView = new KeyOnlyRelationView<>(person);
402 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 380 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
403 381
404 var p1 = new Variable("p1"); 382 var p1 = Variable.of("p1");
405 var p2 = new Variable("p2"); 383 var p2 = Variable.of("p2");
406 var predicate = Dnf.builder("NegativePatternCall") 384 var predicate = Query.builder("NegativePatternCall")
407 .parameters(p1, p2) 385 .parameters(p1, p2)
408 .clause( 386 .clause(
409 personView.call(p1), 387 personView.call(p1),
@@ -415,6 +393,7 @@ class QueryTest {
415 var store = ModelStore.builder() 393 var store = ModelStore.builder()
416 .symbols(person, friend) 394 .symbols(person, friend)
417 .with(ViatraModelQuery.ADAPTER) 395 .with(ViatraModelQuery.ADAPTER)
396 .defaultHint(hint)
418 .queries(predicate) 397 .queries(predicate)
419 .build(); 398 .build();
420 399
@@ -433,18 +412,29 @@ class QueryTest {
433 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 412 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
434 413
435 queryEngine.flushChanges(); 414 queryEngine.flushChanges();
436 assertEquals(6, predicateResultSet.countResults()); 415 assertResults(Map.of(
416 Tuple.of(0, 0), true,
417 Tuple.of(0, 2), true,
418 Tuple.of(1, 1), true,
419 Tuple.of(2, 0), true,
420 Tuple.of(2, 1), true,
421 Tuple.of(2, 2), true,
422 Tuple.of(0, 1), false,
423 Tuple.of(1, 0), false,
424 Tuple.of(1, 2), false,
425 Tuple.of(0, 3), false
426 ), predicateResultSet);
437 } 427 }
438 428
439 @Test 429 @QueryEngineTest
440 void negativePatternCallTest() { 430 void negativePatternCallTest(QueryEvaluationHint hint) {
441 var person = new Symbol<>("Person", 1, Boolean.class, false); 431 var person = new Symbol<>("Person", 1, Boolean.class, false);
442 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 432 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
443 var personView = new KeyOnlyRelationView<>(person); 433 var personView = new KeyOnlyRelationView<>(person);
444 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 434 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
445 435
446 var p1 = new Variable("p1"); 436 var p1 = Variable.of("p1");
447 var p2 = new Variable("p2"); 437 var p2 = Variable.of("p2");
448 var friendPredicate = Dnf.builder("RelationConstraint") 438 var friendPredicate = Dnf.builder("RelationConstraint")
449 .parameters(p1, p2) 439 .parameters(p1, p2)
450 .clause( 440 .clause(
@@ -454,9 +444,9 @@ class QueryTest {
454 ) 444 )
455 .build(); 445 .build();
456 446
457 var p3 = new Variable("p3"); 447 var p3 = Variable.of("p3");
458 var p4 = new Variable("p4"); 448 var p4 = Variable.of("p4");
459 var predicate = Dnf.builder("NegativePatternCall") 449 var predicate = Query.builder("NegativePatternCall")
460 .parameters(p3, p4) 450 .parameters(p3, p4)
461 .clause( 451 .clause(
462 personView.call(p3), 452 personView.call(p3),
@@ -468,6 +458,7 @@ class QueryTest {
468 var store = ModelStore.builder() 458 var store = ModelStore.builder()
469 .symbols(person, friend) 459 .symbols(person, friend)
470 .with(ViatraModelQuery.ADAPTER) 460 .with(ViatraModelQuery.ADAPTER)
461 .defaultHint(hint)
471 .queries(predicate) 462 .queries(predicate)
472 .build(); 463 .build();
473 464
@@ -486,20 +477,31 @@ class QueryTest {
486 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 477 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
487 478
488 queryEngine.flushChanges(); 479 queryEngine.flushChanges();
489 assertEquals(6, predicateResultSet.countResults()); 480 assertResults(Map.of(
481 Tuple.of(0, 0), true,
482 Tuple.of(0, 2), true,
483 Tuple.of(1, 1), true,
484 Tuple.of(2, 0), true,
485 Tuple.of(2, 1), true,
486 Tuple.of(2, 2), true,
487 Tuple.of(0, 1), false,
488 Tuple.of(1, 0), false,
489 Tuple.of(1, 2), false,
490 Tuple.of(0, 3), false
491 ), predicateResultSet);
490 } 492 }
491 493
492 @Test 494 @QueryEngineTest
493 void negativeRelationViewWithQuantificationTest() { 495 void negativeRelationViewWithQuantificationTest(QueryEvaluationHint hint) {
494 var person = new Symbol<>("Person", 1, Boolean.class, false); 496 var person = new Symbol<>("Person", 1, Boolean.class, false);
495 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 497 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
496 var personView = new KeyOnlyRelationView<>(person); 498 var personView = new KeyOnlyRelationView<>(person);
497 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 499 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
498 500
499 var p1 = new Variable("p1"); 501 var p1 = Variable.of("p1");
500 var p2 = new Variable("p2"); 502 var p2 = Variable.of("p2");
501 503
502 var predicate = Dnf.builder("Count") 504 var predicate = Query.builder("Count")
503 .parameters(p1) 505 .parameters(p1)
504 .clause( 506 .clause(
505 personView.call(p1), 507 personView.call(p1),
@@ -510,6 +512,7 @@ class QueryTest {
510 var store = ModelStore.builder() 512 var store = ModelStore.builder()
511 .symbols(person, friend) 513 .symbols(person, friend)
512 .with(ViatraModelQuery.ADAPTER) 514 .with(ViatraModelQuery.ADAPTER)
515 .defaultHint(hint)
513 .queries(predicate) 516 .queries(predicate)
514 .build(); 517 .build();
515 518
@@ -527,18 +530,23 @@ class QueryTest {
527 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE); 530 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
528 531
529 queryEngine.flushChanges(); 532 queryEngine.flushChanges();
530 assertEquals(2, predicateResultSet.countResults()); 533 assertResults(Map.of(
534 Tuple.of(0), false,
535 Tuple.of(1), true,
536 Tuple.of(2), true,
537 Tuple.of(3), false
538 ), predicateResultSet);
531 } 539 }
532 540
533 @Test 541 @QueryEngineTest
534 void negativeWithQuantificationTest() { 542 void negativeWithQuantificationTest(QueryEvaluationHint hint) {
535 var person = new Symbol<>("Person", 1, Boolean.class, false); 543 var person = new Symbol<>("Person", 1, Boolean.class, false);
536 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 544 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
537 var personView = new KeyOnlyRelationView<>(person); 545 var personView = new KeyOnlyRelationView<>(person);
538 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 546 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
539 547
540 var p1 = new Variable("p1"); 548 var p1 = Variable.of("p1");
541 var p2 = new Variable("p2"); 549 var p2 = Variable.of("p2");
542 550
543 var called = Dnf.builder("Called") 551 var called = Dnf.builder("Called")
544 .parameters(p1, p2) 552 .parameters(p1, p2)
@@ -549,7 +557,7 @@ class QueryTest {
549 ) 557 )
550 .build(); 558 .build();
551 559
552 var predicate = Dnf.builder("Count") 560 var predicate = Query.builder("Count")
553 .parameters(p1) 561 .parameters(p1)
554 .clause( 562 .clause(
555 personView.call(p1), 563 personView.call(p1),
@@ -560,6 +568,7 @@ class QueryTest {
560 var store = ModelStore.builder() 568 var store = ModelStore.builder()
561 .symbols(person, friend) 569 .symbols(person, friend)
562 .with(ViatraModelQuery.ADAPTER) 570 .with(ViatraModelQuery.ADAPTER)
571 .defaultHint(hint)
563 .queries(predicate) 572 .queries(predicate)
564 .build(); 573 .build();
565 574
@@ -577,19 +586,24 @@ class QueryTest {
577 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE); 586 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
578 587
579 queryEngine.flushChanges(); 588 queryEngine.flushChanges();
580 assertEquals(2, predicateResultSet.countResults()); 589 assertResults(Map.of(
590 Tuple.of(0), false,
591 Tuple.of(1), true,
592 Tuple.of(2), true,
593 Tuple.of(3), false
594 ), predicateResultSet);
581 } 595 }
582 596
583 @Test 597 @QueryEngineTest
584 void transitiveRelationViewTest() { 598 void transitiveRelationViewTest(QueryEvaluationHint hint) {
585 var person = new Symbol<>("Person", 1, Boolean.class, false); 599 var person = new Symbol<>("Person", 1, Boolean.class, false);
586 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 600 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
587 var personView = new KeyOnlyRelationView<>(person); 601 var personView = new KeyOnlyRelationView<>(person);
588 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 602 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
589 603
590 var p1 = new Variable("p1"); 604 var p1 = Variable.of("p1");
591 var p2 = new Variable("p2"); 605 var p2 = Variable.of("p2");
592 var predicate = Dnf.builder("TransitivePatternCall") 606 var predicate = Query.builder("TransitivePatternCall")
593 .parameters(p1, p2) 607 .parameters(p1, p2)
594 .clause( 608 .clause(
595 personView.call(p1), 609 personView.call(p1),
@@ -601,6 +615,7 @@ class QueryTest {
601 var store = ModelStore.builder() 615 var store = ModelStore.builder()
602 .symbols(person, friend) 616 .symbols(person, friend)
603 .with(ViatraModelQuery.ADAPTER) 617 .with(ViatraModelQuery.ADAPTER)
618 .defaultHint(hint)
604 .queries(predicate) 619 .queries(predicate)
605 .build(); 620 .build();
606 621
@@ -618,18 +633,29 @@ class QueryTest {
618 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 633 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
619 634
620 queryEngine.flushChanges(); 635 queryEngine.flushChanges();
621 assertEquals(3, predicateResultSet.countResults()); 636 assertResults(Map.of(
637 Tuple.of(0, 0), false,
638 Tuple.of(0, 1), true,
639 Tuple.of(0, 2), true,
640 Tuple.of(1, 0), false,
641 Tuple.of(1, 1), false,
642 Tuple.of(1, 2), true,
643 Tuple.of(2, 0), false,
644 Tuple.of(2, 1), false,
645 Tuple.of(2, 2), false,
646 Tuple.of(2, 3), false
647 ), predicateResultSet);
622 } 648 }
623 649
624 @Test 650 @QueryEngineTest
625 void transitivePatternCallTest() { 651 void transitivePatternCallTest(QueryEvaluationHint hint) {
626 var person = new Symbol<>("Person", 1, Boolean.class, false); 652 var person = new Symbol<>("Person", 1, Boolean.class, false);
627 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 653 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
628 var personView = new KeyOnlyRelationView<>(person); 654 var personView = new KeyOnlyRelationView<>(person);
629 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 655 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
630 656
631 var p1 = new Variable("p1"); 657 var p1 = Variable.of("p1");
632 var p2 = new Variable("p2"); 658 var p2 = Variable.of("p2");
633 var friendPredicate = Dnf.builder("RelationConstraint") 659 var friendPredicate = Dnf.builder("RelationConstraint")
634 .parameters(p1, p2) 660 .parameters(p1, p2)
635 .clause( 661 .clause(
@@ -639,9 +665,9 @@ class QueryTest {
639 ) 665 )
640 .build(); 666 .build();
641 667
642 var p3 = new Variable("p3"); 668 var p3 = Variable.of("p3");
643 var p4 = new Variable("p4"); 669 var p4 = Variable.of("p4");
644 var predicate = Dnf.builder("TransitivePatternCall") 670 var predicate = Query.builder("TransitivePatternCall")
645 .parameters(p3, p4) 671 .parameters(p3, p4)
646 .clause( 672 .clause(
647 personView.call(p3), 673 personView.call(p3),
@@ -653,6 +679,7 @@ class QueryTest {
653 var store = ModelStore.builder() 679 var store = ModelStore.builder()
654 .symbols(person, friend) 680 .symbols(person, friend)
655 .with(ViatraModelQuery.ADAPTER) 681 .with(ViatraModelQuery.ADAPTER)
682 .defaultHint(hint)
656 .queries(predicate) 683 .queries(predicate)
657 .build(); 684 .build();
658 685
@@ -670,15 +697,71 @@ class QueryTest {
670 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 697 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
671 698
672 queryEngine.flushChanges(); 699 queryEngine.flushChanges();
673 assertEquals(3, predicateResultSet.countResults()); 700 assertResults(Map.of(
701 Tuple.of(0, 0), false,
702 Tuple.of(0, 1), true,
703 Tuple.of(0, 2), true,
704 Tuple.of(1, 0), false,
705 Tuple.of(1, 1), false,
706 Tuple.of(1, 2), true,
707 Tuple.of(2, 0), false,
708 Tuple.of(2, 1), false,
709 Tuple.of(2, 2), false,
710 Tuple.of(2, 3), false
711 ), predicateResultSet);
712 }
713
714 @QueryEngineTest
715 void assumeTest(QueryEvaluationHint hint) {
716 var person = new Symbol<>("Person", 1, Boolean.class, false);
717 var age = new Symbol<>("age", 1, Integer.class, null);
718 var personView = new KeyOnlyRelationView<>(person);
719 var ageView = new FunctionalRelationView<>(age);
720
721 var p1 = Variable.of("p1");
722 var x = Variable.of("x", Integer.class);
723 var query = Query.builder("Constraint")
724 .parameter(p1)
725 .clause(
726 personView.call(p1),
727 ageView.call(p1, x),
728 assume(greaterEq(x, constant(18)))
729 )
730 .build();
731
732 var store = ModelStore.builder()
733 .symbols(person, age)
734 .with(ViatraModelQuery.ADAPTER)
735 .defaultHint(hint)
736 .queries(query)
737 .build();
738
739 var model = store.createEmptyModel();
740 var personInterpretation = model.getInterpretation(person);
741 var ageInterpretation = model.getInterpretation(age);
742 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
743 var queryResultSet = queryEngine.getResultSet(query);
744
745 personInterpretation.put(Tuple.of(0), true);
746 personInterpretation.put(Tuple.of(1), true);
747
748 ageInterpretation.put(Tuple.of(0), 12);
749 ageInterpretation.put(Tuple.of(1), 24);
750
751 queryEngine.flushChanges();
752 assertResults(Map.of(
753 Tuple.of(0), false,
754 Tuple.of(1), true,
755 Tuple.of(2), false
756 ), queryResultSet);
674 } 757 }
675 758
676 @Test 759 @Test
677 void alwaysFalseTest() { 760 void alwaysFalseTest() {
678 var person = new Symbol<>("Person", 1, Boolean.class, false); 761 var person = new Symbol<>("Person", 1, Boolean.class, false);
679 762
680 var p1 = new Variable("p1"); 763 var p1 = Variable.of("p1");
681 var predicate = Dnf.builder("AlwaysFalse").parameters(p1).build(); 764 var predicate = Query.builder("AlwaysFalse").parameters(p1).build();
682 765
683 var store = ModelStore.builder() 766 var store = ModelStore.builder()
684 .symbols(person) 767 .symbols(person)
@@ -696,28 +779,19 @@ class QueryTest {
696 personInterpretation.put(Tuple.of(2), true); 779 personInterpretation.put(Tuple.of(2), true);
697 780
698 queryEngine.flushChanges(); 781 queryEngine.flushChanges();
699 assertEquals(0, predicateResultSet.countResults()); 782 assertResults(Map.of(), predicateResultSet);
700 } 783 }
701 784
702 @Test 785 @Test
703 void alwaysTrueTest() { 786 void alwaysTrueTest() {
704 var person = new Symbol<>("Person", 1, Boolean.class, false); 787 var person = new Symbol<>("Person", 1, Boolean.class, false);
705 788
706 var p1 = new Variable("p1"); 789 var p1 = Variable.of("p1");
707 var predicate = Dnf.builder("AlwaysTrue").parameters(p1).clause().build(); 790 var predicate = Query.builder("AlwaysTrue").parameters(p1).clause().build();
708 791
709 var storeBuilder = ModelStore.builder().symbols(person); 792 var storeBuilder = ModelStore.builder().symbols(person);
710 var queryBuilder = storeBuilder.with(ViatraModelQuery.ADAPTER); 793 var queryBuilder = storeBuilder.with(ViatraModelQuery.ADAPTER);
711 794
712 assertThrows(IllegalArgumentException.class, () -> queryBuilder.queries(predicate)); 795 assertThrows(IllegalArgumentException.class, () -> queryBuilder.queries(predicate));
713 } 796 }
714
715 private static void compareMatchSets(Cursor<TupleLike, Boolean> cursor, Set<Tuple> expected) {
716 Set<Tuple> translatedMatchSet = new HashSet<>();
717 while (cursor.move()) {
718 var element = cursor.getKey();
719 translatedMatchSet.add(element.toTuple());
720 }
721 assertEquals(expected, translatedMatchSet);
722 }
723} 797}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
index 461685b5..abd49341 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
@@ -1,25 +1,160 @@
1package tools.refinery.store.query.viatra; 1package tools.refinery.store.query.viatra;
2 2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import org.junit.jupiter.api.Disabled;
3import org.junit.jupiter.api.Test; 5import org.junit.jupiter.api.Test;
4import tools.refinery.store.model.ModelStore; 6import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.Dnf;
6import tools.refinery.store.query.ModelQuery; 7import tools.refinery.store.query.ModelQuery;
7import tools.refinery.store.query.Variable; 8import tools.refinery.store.query.dnf.Query;
9import tools.refinery.store.query.term.Variable;
10import tools.refinery.store.query.view.FilteredRelationView;
11import tools.refinery.store.query.view.FunctionalRelationView;
8import tools.refinery.store.query.view.KeyOnlyRelationView; 12import tools.refinery.store.query.view.KeyOnlyRelationView;
9import tools.refinery.store.representation.Symbol; 13import tools.refinery.store.representation.Symbol;
10import tools.refinery.store.tuple.Tuple; 14import tools.refinery.store.tuple.Tuple;
11 15
12import static org.junit.jupiter.api.Assertions.*; 16import java.util.Map;
17import java.util.Optional;
18
19import static org.junit.jupiter.api.Assertions.assertFalse;
20import static org.junit.jupiter.api.Assertions.assertTrue;
21import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
22import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
13 23
14class QueryTransactionTest { 24class QueryTransactionTest {
15 @Test 25 @Test
16 void flushTest() { 26 void flushTest() {
17 var person = new Symbol<>("Person", 1, Boolean.class, false); 27 var person = new Symbol<>("Person", 1, Boolean.class, false);
28 var personView = new KeyOnlyRelationView<>(person);
29
30 var p1 = Variable.of("p1");
31 var predicate = Query.builder("TypeConstraint")
32 .parameters(p1)
33 .clause(personView.call(p1))
34 .build();
35
36 var store = ModelStore.builder()
37 .symbols(person)
38 .with(ViatraModelQuery.ADAPTER)
39 .queries(predicate)
40 .build();
41
42 var model = store.createEmptyModel();
43 var personInterpretation = model.getInterpretation(person);
44 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
45 var predicateResultSet = queryEngine.getResultSet(predicate);
46
47 assertResults(Map.of(
48 Tuple.of(0), false,
49 Tuple.of(1), false,
50 Tuple.of(2), false,
51 Tuple.of(3), false
52 ), predicateResultSet);
53 assertFalse(queryEngine.hasPendingChanges());
54
55 personInterpretation.put(Tuple.of(0), true);
56 personInterpretation.put(Tuple.of(1), true);
57
58 assertResults(Map.of(
59 Tuple.of(0), false,
60 Tuple.of(1), false,
61 Tuple.of(2), false,
62 Tuple.of(3), false
63 ), predicateResultSet);
64 assertTrue(queryEngine.hasPendingChanges());
65
66 queryEngine.flushChanges();
67 assertResults(Map.of(
68 Tuple.of(0), true,
69 Tuple.of(1), true,
70 Tuple.of(2), false,
71 Tuple.of(3), false
72 ), predicateResultSet);
73 assertFalse(queryEngine.hasPendingChanges());
74
75 personInterpretation.put(Tuple.of(1), false);
76 personInterpretation.put(Tuple.of(2), true);
77
78 assertResults(Map.of(
79 Tuple.of(0), true,
80 Tuple.of(1), true,
81 Tuple.of(2), false,
82 Tuple.of(3), false
83 ), predicateResultSet);
84 assertTrue(queryEngine.hasPendingChanges());
85
86 queryEngine.flushChanges();
87 assertResults(Map.of(
88 Tuple.of(0), true,
89 Tuple.of(1), false,
90 Tuple.of(2), true,
91 Tuple.of(3), false
92 ), predicateResultSet);
93 assertFalse(queryEngine.hasPendingChanges());
94 }
95
96 @Test
97 void localSearchTest() {
98 var person = new Symbol<>("Person", 1, Boolean.class, false);
99 var personView = new KeyOnlyRelationView<>(person);
100
101 var p1 = Variable.of("p1");
102 var predicate = Query.builder("TypeConstraint")
103 .parameters(p1)
104 .clause(personView.call(p1))
105 .build();
106
107 var store = ModelStore.builder()
108 .symbols(person)
109 .with(ViatraModelQuery.ADAPTER)
110 .defaultHint(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH))
111 .queries(predicate)
112 .build();
113
114 var model = store.createEmptyModel();
115 var personInterpretation = model.getInterpretation(person);
116 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
117 var predicateResultSet = queryEngine.getResultSet(predicate);
118
119 assertResults(Map.of(
120 Tuple.of(0), false,
121 Tuple.of(1), false,
122 Tuple.of(2), false,
123 Tuple.of(3), false
124 ), predicateResultSet);
125 assertFalse(queryEngine.hasPendingChanges());
126
127 personInterpretation.put(Tuple.of(0), true);
128 personInterpretation.put(Tuple.of(1), true);
129
130 assertResults(Map.of(
131 Tuple.of(0), true,
132 Tuple.of(1), true,
133 Tuple.of(2), false,
134 Tuple.of(3), false
135 ), predicateResultSet);
136 assertFalse(queryEngine.hasPendingChanges());
137
138 personInterpretation.put(Tuple.of(1), false);
139 personInterpretation.put(Tuple.of(2), true);
140
141 assertResults(Map.of(
142 Tuple.of(0), true,
143 Tuple.of(1), false,
144 Tuple.of(2), true,
145 Tuple.of(3), false
146 ), predicateResultSet);
147 assertFalse(queryEngine.hasPendingChanges());
148 }
149
150 @Test
151 void unrelatedChangesTest() {
152 var person = new Symbol<>("Person", 1, Boolean.class, false);
18 var asset = new Symbol<>("Asset", 1, Boolean.class, false); 153 var asset = new Symbol<>("Asset", 1, Boolean.class, false);
19 var personView = new KeyOnlyRelationView<>(person); 154 var personView = new KeyOnlyRelationView<>(person);
20 155
21 var p1 = new Variable("p1"); 156 var p1 = Variable.of("p1");
22 var predicate = Dnf.builder("TypeConstraint") 157 var predicate = Query.builder("TypeConstraint")
23 .parameters(p1) 158 .parameters(p1)
24 .clause(personView.call(p1)) 159 .clause(personView.call(p1))
25 .build(); 160 .build();
@@ -36,7 +171,6 @@ class QueryTransactionTest {
36 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 171 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
37 var predicateResultSet = queryEngine.getResultSet(predicate); 172 var predicateResultSet = queryEngine.getResultSet(predicate);
38 173
39 assertEquals(0, predicateResultSet.countResults());
40 assertFalse(queryEngine.hasPendingChanges()); 174 assertFalse(queryEngine.hasPendingChanges());
41 175
42 personInterpretation.put(Tuple.of(0), true); 176 personInterpretation.put(Tuple.of(0), true);
@@ -45,19 +179,245 @@ class QueryTransactionTest {
45 assetInterpretation.put(Tuple.of(1), true); 179 assetInterpretation.put(Tuple.of(1), true);
46 assetInterpretation.put(Tuple.of(2), true); 180 assetInterpretation.put(Tuple.of(2), true);
47 181
48 assertEquals(0, predicateResultSet.countResults()); 182 assertResults(Map.of(
183 Tuple.of(0), false,
184 Tuple.of(1), false,
185 Tuple.of(2), false,
186 Tuple.of(3), false,
187 Tuple.of(4), false
188 ), predicateResultSet);
49 assertTrue(queryEngine.hasPendingChanges()); 189 assertTrue(queryEngine.hasPendingChanges());
50 190
51 queryEngine.flushChanges(); 191 queryEngine.flushChanges();
52 assertEquals(2, predicateResultSet.countResults()); 192 assertResults(Map.of(
193 Tuple.of(0), true,
194 Tuple.of(1), true,
195 Tuple.of(2), false,
196 Tuple.of(3), false,
197 Tuple.of(4), false
198 ), predicateResultSet);
53 assertFalse(queryEngine.hasPendingChanges()); 199 assertFalse(queryEngine.hasPendingChanges());
54 200
55 personInterpretation.put(Tuple.of(4), true); 201 assetInterpretation.put(Tuple.of(3), true);
56 assertEquals(2, predicateResultSet.countResults()); 202 assertFalse(queryEngine.hasPendingChanges());
57 assertTrue(queryEngine.hasPendingChanges()); 203
204 assertResults(Map.of(
205 Tuple.of(0), true,
206 Tuple.of(1), true,
207 Tuple.of(2), false,
208 Tuple.of(3), false,
209 Tuple.of(4), false
210 ), predicateResultSet);
211
212 queryEngine.flushChanges();
213 assertResults(Map.of(
214 Tuple.of(0), true,
215 Tuple.of(1), true,
216 Tuple.of(2), false,
217 Tuple.of(3), false,
218 Tuple.of(4), false
219 ), predicateResultSet);
220 assertFalse(queryEngine.hasPendingChanges());
221 }
222
223 @Test
224 void tupleChangingChangeTest() {
225 var person = new Symbol<>("Person", 1, Boolean.class, false);
226 var age = new Symbol<>("age", 1, Integer.class, null);
227 var personView = new KeyOnlyRelationView<>(person);
228 var ageView = new FunctionalRelationView<>(age);
229
230 var p1 = Variable.of("p1");
231 var x = Variable.of("x", Integer.class);
232 var query = Query.builder()
233 .parameter(p1)
234 .output(x)
235 .clause(
236 personView.call(p1),
237 ageView.call(p1, x)
238 )
239 .build();
240
241 var store = ModelStore.builder()
242 .symbols(person, age)
243 .with(ViatraModelQuery.ADAPTER)
244 .query(query)
245 .build();
246
247 var model = store.createEmptyModel();
248 var personInterpretation = model.getInterpretation(person);
249 var ageInterpretation = model.getInterpretation(age);
250 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
251 var queryResultSet = queryEngine.getResultSet(query);
252
253 personInterpretation.put(Tuple.of(0), true);
254
255 ageInterpretation.put(Tuple.of(0), 24);
256
257 queryEngine.flushChanges();
258 assertResults(Map.of(Tuple.of(0), 24), queryResultSet);
259
260 ageInterpretation.put(Tuple.of(0), 25);
261
262 queryEngine.flushChanges();
263 assertResults(Map.of(Tuple.of(0), 25), queryResultSet);
264
265 ageInterpretation.put(Tuple.of(0), null);
266
267 queryEngine.flushChanges();
268 assertNullableResults(Map.of(Tuple.of(0), Optional.empty()), queryResultSet);
269 }
270
271 @Test
272 void tuplePreservingUnchangedTest() {
273 var person = new Symbol<>("Person", 1, Boolean.class, false);
274 var age = new Symbol<>("age", 1, Integer.class, null);
275 var personView = new KeyOnlyRelationView<>(person);
276 var adultView = new FilteredRelationView<>(age, "adult", n -> n != null && n >= 18);
277
278 var p1 = Variable.of("p1");
279 var x = Variable.of("x", Integer.class);
280 var query = Query.builder()
281 .parameter(p1)
282 .clause(
283 personView.call(p1),
284 adultView.call(p1)
285 )
286 .build();
287
288 var store = ModelStore.builder()
289 .symbols(person, age)
290 .with(ViatraModelQuery.ADAPTER)
291 .query(query)
292 .build();
293
294 var model = store.createEmptyModel();
295 var personInterpretation = model.getInterpretation(person);
296 var ageInterpretation = model.getInterpretation(age);
297 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
298 var queryResultSet = queryEngine.getResultSet(query);
299
300 personInterpretation.put(Tuple.of(0), true);
301
302 ageInterpretation.put(Tuple.of(0), 24);
303
304 queryEngine.flushChanges();
305 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
306
307 ageInterpretation.put(Tuple.of(0), 25);
308
309 queryEngine.flushChanges();
310 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
311
312 ageInterpretation.put(Tuple.of(0), 17);
58 313
59 queryEngine.flushChanges(); 314 queryEngine.flushChanges();
60 assertEquals(3, predicateResultSet.countResults()); 315 assertResults(Map.of(Tuple.of(0), false), queryResultSet);
316 }
317
318 @Disabled("TODO Fix DiffCursor")
319 @Test
320 void commitAfterFlushTest() {
321 var person = new Symbol<>("Person", 1, Boolean.class, false);
322 var personView = new KeyOnlyRelationView<>(person);
323
324 var p1 = Variable.of("p1");
325 var predicate = Query.builder("TypeConstraint")
326 .parameters(p1)
327 .clause(personView.call(p1))
328 .build();
329
330 var store = ModelStore.builder()
331 .symbols(person)
332 .with(ViatraModelQuery.ADAPTER)
333 .queries(predicate)
334 .build();
335
336 var model = store.createEmptyModel();
337 var personInterpretation = model.getInterpretation(person);
338 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
339 var predicateResultSet = queryEngine.getResultSet(predicate);
340
341 personInterpretation.put(Tuple.of(0), true);
342 personInterpretation.put(Tuple.of(1), true);
343
344 queryEngine.flushChanges();
345 assertResults(Map.of(
346 Tuple.of(0), true,
347 Tuple.of(1), true,
348 Tuple.of(2), false,
349 Tuple.of(3), false
350 ), predicateResultSet);
351
352 var state1 = model.commit();
353
354 personInterpretation.put(Tuple.of(1), false);
355 personInterpretation.put(Tuple.of(2), true);
356
357 queryEngine.flushChanges();
358 assertResults(Map.of(
359 Tuple.of(0), true,
360 Tuple.of(1), false,
361 Tuple.of(2), true,
362 Tuple.of(3), false
363 ), predicateResultSet);
364
365 model.restore(state1);
366
367 assertFalse(queryEngine.hasPendingChanges());
368 assertResults(Map.of(
369 Tuple.of(0), true,
370 Tuple.of(1), true,
371 Tuple.of(2), false,
372 Tuple.of(3), false
373 ), predicateResultSet);
374 }
375
376 @Disabled("TODO Fix DiffCursor")
377 @Test
378 void commitWithoutFlushTest() {
379 var person = new Symbol<>("Person", 1, Boolean.class, false);
380 var personView = new KeyOnlyRelationView<>(person);
381
382 var p1 = Variable.of("p1");
383 var predicate = Query.builder("TypeConstraint")
384 .parameters(p1)
385 .clause(personView.call(p1))
386 .build();
387
388 var store = ModelStore.builder()
389 .symbols(person)
390 .with(ViatraModelQuery.ADAPTER)
391 .queries(predicate)
392 .build();
393
394 var model = store.createEmptyModel();
395 var personInterpretation = model.getInterpretation(person);
396 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
397 var predicateResultSet = queryEngine.getResultSet(predicate);
398
399 personInterpretation.put(Tuple.of(0), true);
400 personInterpretation.put(Tuple.of(1), true);
401
402 assertResults(Map.of(), predicateResultSet);
403 assertTrue(queryEngine.hasPendingChanges());
404
405 var state1 = model.commit();
406
407 personInterpretation.put(Tuple.of(1), false);
408 personInterpretation.put(Tuple.of(2), true);
409
410 assertResults(Map.of(), predicateResultSet);
411 assertTrue(queryEngine.hasPendingChanges());
412
413 model.restore(state1);
414
415 assertResults(Map.of(
416 Tuple.of(0), true,
417 Tuple.of(1), true,
418 Tuple.of(2), false,
419 Tuple.of(3), false
420 ), predicateResultSet);
61 assertFalse(queryEngine.hasPendingChanges()); 421 assertFalse(queryEngine.hasPendingChanges());
62 } 422 }
63} 423}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java
new file mode 100644
index 00000000..6f50ec73
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java
@@ -0,0 +1,52 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.junit.jupiter.api.function.Executable;
4import tools.refinery.store.query.ResultSet;
5import tools.refinery.store.tuple.Tuple;
6
7import java.util.*;
8
9import static org.hamcrest.MatcherAssert.assertThat;
10import static org.hamcrest.Matchers.is;
11import static org.hamcrest.Matchers.nullValue;
12import static org.junit.jupiter.api.Assertions.assertAll;
13
14public final class QueryAssertions {
15 private QueryAssertions() {
16 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
17 }
18
19 public static <T> void assertNullableResults(Map<Tuple, Optional<T>> expected, ResultSet<T> resultSet) {
20 var nullableValuesMap = new LinkedHashMap<Tuple, T>(expected.size());
21 for (var entry : expected.entrySet()) {
22 nullableValuesMap.put(entry.getKey(), entry.getValue().orElse(null));
23 }
24 assertResults(nullableValuesMap, resultSet);
25 }
26
27 public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) {
28 var defaultValue = resultSet.getQuery().defaultValue();
29 var filteredExpected = new LinkedHashMap<Tuple, T>();
30 var executables = new ArrayList<Executable>();
31 for (var entry : expected.entrySet()) {
32 var key = entry.getKey();
33 var value = entry.getValue();
34 if (!Objects.equals(value, defaultValue)) {
35 filteredExpected.put(key, value);
36 }
37 executables.add(() -> assertThat("value for key " + key,resultSet.get(key), is(value)));
38 }
39 executables.add(() -> assertThat("results size", resultSet.size(), is(filteredExpected.size())));
40
41 var actual = new LinkedHashMap<Tuple, T>();
42 var cursor = resultSet.getAll();
43 while (cursor.move()) {
44 var key = cursor.getKey();
45 var previous = actual.put(key.toTuple(), cursor.getValue());
46 assertThat("duplicate value for key " + key, previous, nullValue());
47 }
48 executables.add(() -> assertThat("results cursor", actual, is(filteredExpected)));
49
50 assertAll(executables);
51 }
52}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
new file mode 100644
index 00000000..b1818a17
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4
5/**
6 * Overrides {@link QueryEvaluationHint#toString()} for pretty names in parametric test names.
7 */
8class QueryBackendHint extends QueryEvaluationHint {
9 public QueryBackendHint(BackendRequirement backendRequirementType) {
10 super(null, backendRequirementType);
11 }
12
13 @Override
14 public String toString() {
15 return switch (getQueryBackendRequirementType()) {
16 case UNSPECIFIED -> "default";
17 case DEFAULT_CACHING -> "incremental";
18 case DEFAULT_SEARCH -> "localSearch";
19 default -> throw new IllegalStateException("Unknown BackendRequirement");
20 };
21 }
22}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java
new file mode 100644
index 00000000..f129520c
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.junit.jupiter.params.ParameterizedTest;
4import org.junit.jupiter.params.provider.ArgumentsSource;
5
6import java.lang.annotation.ElementType;
7import java.lang.annotation.Retention;
8import java.lang.annotation.RetentionPolicy;
9import java.lang.annotation.Target;
10
11@ParameterizedTest(name = "backend = {0}")
12@ArgumentsSource(QueryEvaluationHintSource.class)
13@Target(ElementType.METHOD)
14@Retention(RetentionPolicy.RUNTIME)
15public @interface QueryEngineTest {
16}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
new file mode 100644
index 00000000..a55762e2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
@@ -0,0 +1,19 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import org.junit.jupiter.api.extension.ExtensionContext;
5import org.junit.jupiter.params.provider.Arguments;
6import org.junit.jupiter.params.provider.ArgumentsProvider;
7
8import java.util.stream.Stream;
9
10public class QueryEvaluationHintSource implements ArgumentsProvider {
11 @Override
12 public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
13 return Stream.of(
14 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.UNSPECIFIED)),
15 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.DEFAULT_CACHING)),
16 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH))
17 );
18 }
19}