diff options
Diffstat (limited to 'subprojects')
18 files changed, 154 insertions, 72 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 931a07aa..66279c94 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 | |||
@@ -9,9 +9,10 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; | |||
9 | import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; | 9 | import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; |
10 | import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; | 10 | import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; |
11 | import tools.refinery.store.model.ModelStore; | 11 | import tools.refinery.store.model.ModelStore; |
12 | import tools.refinery.store.query.ModelQueryBuilder; | ||
12 | import tools.refinery.store.query.dnf.AnyQuery; | 13 | import tools.refinery.store.query.dnf.AnyQuery; |
13 | import tools.refinery.store.query.dnf.Dnf; | 14 | import tools.refinery.store.query.dnf.Dnf; |
14 | import tools.refinery.store.query.ModelQueryBuilder; | 15 | import tools.refinery.store.query.rewriter.DnfRewriter; |
15 | 16 | ||
16 | import java.util.Collection; | 17 | import java.util.Collection; |
17 | import java.util.function.Function; | 18 | import java.util.function.Function; |
@@ -43,12 +44,11 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder { | |||
43 | @Override | 44 | @Override |
44 | ViatraModelQueryBuilder query(AnyQuery query); | 45 | ViatraModelQueryBuilder query(AnyQuery query); |
45 | 46 | ||
46 | ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint); | 47 | @Override |
48 | ViatraModelQueryBuilder rewriter(DnfRewriter rewriter); | ||
47 | 49 | ||
48 | ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint); | 50 | ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint); |
49 | 51 | ||
50 | ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint); | ||
51 | |||
52 | @Override | 52 | @Override |
53 | ViatraModelQueryStoreAdapter build(ModelStore store); | 53 | ViatraModelQueryStoreAdapter build(ModelStore store); |
54 | } | 54 | } |
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 5f3e86b4..e17386e1 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 | |||
@@ -131,7 +131,8 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, Mod | |||
131 | 131 | ||
132 | @Override | 132 | @Override |
133 | public <T> ResultSet<T> getResultSet(Query<T> query) { | 133 | public <T> ResultSet<T> getResultSet(Query<T> query) { |
134 | var resultSet = resultSets.get(query); | 134 | var canonicalQuery = storeAdapter.getCanonicalQuery(query); |
135 | var resultSet = resultSets.get(canonicalQuery); | ||
135 | if (resultSet == null) { | 136 | if (resultSet == null) { |
136 | throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name())); | 137 | throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name())); |
137 | } | 138 | } |
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 ce2467b4..5ee8bd74 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 | |||
@@ -13,9 +13,12 @@ import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; | |||
13 | import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory; | 13 | import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory; |
14 | import tools.refinery.store.adapter.AbstractModelAdapterBuilder; | 14 | import tools.refinery.store.adapter.AbstractModelAdapterBuilder; |
15 | import tools.refinery.store.model.ModelStore; | 15 | import tools.refinery.store.model.ModelStore; |
16 | import tools.refinery.store.model.ModelStoreBuilder; | ||
17 | import tools.refinery.store.query.dnf.AnyQuery; | 16 | import tools.refinery.store.query.dnf.AnyQuery; |
18 | import tools.refinery.store.query.dnf.Dnf; | 17 | import tools.refinery.store.query.dnf.Dnf; |
18 | import tools.refinery.store.query.rewriter.CompositeRewriter; | ||
19 | import tools.refinery.store.query.rewriter.DnfRewriter; | ||
20 | import tools.refinery.store.query.rewriter.DuplicateDnfRemover; | ||
21 | import tools.refinery.store.query.rewriter.InputParameterResolver; | ||
19 | import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; | 22 | import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; |
20 | import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; | 23 | import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; |
21 | import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory; | 24 | import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory; |
@@ -32,15 +35,18 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via | |||
32 | // Use a cost function that ignores the initial (empty) model but allows higher arity input keys. | 35 | // Use a cost function that ignores the initial (empty) model but allows higher arity input keys. |
33 | LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction() | 36 | LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction() |
34 | ), (IQueryBackendFactory) null); | 37 | ), (IQueryBackendFactory) null); |
38 | private final CompositeRewriter rewriter; | ||
35 | private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); | 39 | private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); |
36 | private final Set<AnyQuery> vacuousQueries = new LinkedHashSet<>(); | 40 | private final Set<AnyQuery> queries = new LinkedHashSet<>(); |
37 | private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>(); | ||
38 | 41 | ||
39 | public ViatraModelQueryBuilderImpl() { | 42 | public ViatraModelQueryBuilderImpl() { |
40 | engineOptionsBuilder = new ViatraQueryEngineOptions.Builder() | 43 | engineOptionsBuilder = new ViatraQueryEngineOptions.Builder() |
41 | .withDefaultBackend(ReteBackendFactory.INSTANCE) | 44 | .withDefaultBackend(ReteBackendFactory.INSTANCE) |
42 | .withDefaultCachingBackend(ReteBackendFactory.INSTANCE) | 45 | .withDefaultCachingBackend(ReteBackendFactory.INSTANCE) |
43 | .withDefaultSearchBackend(RelationalLocalSearchBackendFactory.INSTANCE); | 46 | .withDefaultSearchBackend(RelationalLocalSearchBackendFactory.INSTANCE); |
47 | rewriter = new CompositeRewriter(); | ||
48 | rewriter.addFirst(new DuplicateDnfRemover()); | ||
49 | rewriter.addFirst(new InputParameterResolver()); | ||
44 | } | 50 | } |
45 | 51 | ||
46 | @Override | 52 | @Override |
@@ -79,58 +85,58 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder<Via | |||
79 | } | 85 | } |
80 | 86 | ||
81 | @Override | 87 | @Override |
82 | public ViatraModelQueryBuilder query(AnyQuery query) { | 88 | public ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) { |
83 | checkNotConfigured(); | 89 | checkNotConfigured(); |
84 | if (querySpecifications.containsKey(query) || vacuousQueries.contains(query)) { | 90 | this.queries.addAll(queries); |
85 | // Ignore duplicate queries. | ||
86 | return this; | ||
87 | } | ||
88 | var dnf = query.getDnf(); | ||
89 | var reduction = dnf.getReduction(); | ||
90 | switch (reduction) { | ||
91 | case NOT_REDUCIBLE -> { | ||
92 | var pQuery = dnf2PQuery.translate(dnf); | ||
93 | querySpecifications.put(query, pQuery.build()); | ||
94 | } | ||
95 | case ALWAYS_FALSE -> vacuousQueries.add(query); | ||
96 | case ALWAYS_TRUE -> throw new IllegalArgumentException( | ||
97 | "Query %s is relationally unsafe (it matches every tuple)".formatted(query.name())); | ||
98 | default -> throw new IllegalArgumentException("Unknown reduction: " + reduction); | ||
99 | } | ||
100 | return this; | 91 | return this; |
101 | } | 92 | } |
102 | 93 | ||
103 | @Override | 94 | @Override |
104 | public ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint) { | 95 | public ViatraModelQueryBuilder query(AnyQuery query) { |
105 | hint(query.getDnf(), queryEvaluationHint); | 96 | checkNotConfigured(); |
106 | query(query); | 97 | queries.add(query); |
107 | return this; | 98 | return this; |
108 | } | 99 | } |
109 | 100 | ||
110 | @Override | 101 | @Override |
111 | public ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint) { | 102 | public ViatraModelQueryBuilder rewriter(DnfRewriter rewriter) { |
112 | checkNotConfigured(); | 103 | this.rewriter.addFirst(rewriter); |
113 | dnf2PQuery.setComputeHint(computeHint); | ||
114 | return this; | 104 | return this; |
115 | } | 105 | } |
116 | 106 | ||
117 | @Override | 107 | @Override |
118 | public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) { | 108 | public ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint) { |
119 | checkNotConfigured(); | 109 | checkNotConfigured(); |
120 | dnf2PQuery.hint(dnf, queryEvaluationHint); | 110 | dnf2PQuery.setComputeHint(computeHint); |
121 | return this; | 111 | return this; |
122 | } | 112 | } |
123 | 113 | ||
124 | @Override | 114 | @Override |
125 | public void doConfigure(ModelStoreBuilder storeBuilder) { | ||
126 | dnf2PQuery.assertNoUnusedHints(); | ||
127 | } | ||
128 | |||
129 | @Override | ||
130 | public ViatraModelQueryStoreAdapterImpl doBuild(ModelStore store) { | 115 | public ViatraModelQueryStoreAdapterImpl doBuild(ModelStore store) { |
116 | var canonicalQueryMap = new HashMap<AnyQuery, AnyQuery>(); | ||
117 | var querySpecifications = new LinkedHashMap<AnyQuery, IQuerySpecification<RawPatternMatcher>>(); | ||
118 | var vacuousQueries = new LinkedHashSet<AnyQuery>(); | ||
119 | for (var query : queries) { | ||
120 | var canonicalQuery = rewriter.rewrite(query); | ||
121 | canonicalQueryMap.put(query, canonicalQuery); | ||
122 | var dnf = canonicalQuery.getDnf(); | ||
123 | var reduction = dnf.getReduction(); | ||
124 | switch (reduction) { | ||
125 | case NOT_REDUCIBLE -> { | ||
126 | var pQuery = dnf2PQuery.translate(dnf); | ||
127 | querySpecifications.put(canonicalQuery, pQuery.build()); | ||
128 | } | ||
129 | case ALWAYS_FALSE -> vacuousQueries.add(canonicalQuery); | ||
130 | case ALWAYS_TRUE -> throw new IllegalArgumentException( | ||
131 | "Query %s is relationally unsafe (it matches every tuple)".formatted(query.name())); | ||
132 | default -> throw new IllegalArgumentException("Unknown reduction: " + reduction); | ||
133 | } | ||
134 | } | ||
135 | |||
131 | validateSymbols(store); | 136 | validateSymbols(store); |
132 | return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getSymbolViews(), | 137 | return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getSymbolViews(), |
133 | Collections.unmodifiableMap(querySpecifications), Collections.unmodifiableSet(vacuousQueries)); | 138 | Collections.unmodifiableMap(canonicalQueryMap), Collections.unmodifiableMap(querySpecifications), |
139 | Collections.unmodifiableSet(vacuousQueries)); | ||
134 | } | 140 | } |
135 | 141 | ||
136 | private ViatraQueryEngineOptions buildEngineOptions() { | 142 | private ViatraQueryEngineOptions buildEngineOptions() { |
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 11a3c7fd..25f1cd02 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 | |||
@@ -11,6 +11,7 @@ import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | |||
11 | import tools.refinery.store.model.Model; | 11 | import tools.refinery.store.model.Model; |
12 | import tools.refinery.store.model.ModelStore; | 12 | import tools.refinery.store.model.ModelStore; |
13 | import tools.refinery.store.query.dnf.AnyQuery; | 13 | import tools.refinery.store.query.dnf.AnyQuery; |
14 | import tools.refinery.store.query.dnf.Query; | ||
14 | import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter; | 15 | import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter; |
15 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; | 16 | import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; |
16 | import tools.refinery.store.query.view.AnySymbolView; | 17 | import tools.refinery.store.query.view.AnySymbolView; |
@@ -21,17 +22,20 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd | |||
21 | private final ModelStore store; | 22 | private final ModelStore store; |
22 | private final ViatraQueryEngineOptions engineOptions; | 23 | private final ViatraQueryEngineOptions engineOptions; |
23 | private final Map<AnySymbolView, IInputKey> inputKeys; | 24 | private final Map<AnySymbolView, IInputKey> inputKeys; |
25 | private final Map<AnyQuery, AnyQuery> canonicalQueryMap; | ||
24 | private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications; | 26 | private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications; |
25 | private final Set<AnyQuery> vacuousQueries; | 27 | private final Set<AnyQuery> vacuousQueries; |
26 | private final Set<AnyQuery> allQueries; | 28 | private final Set<AnyQuery> allQueries; |
27 | 29 | ||
28 | ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, | 30 | ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, |
29 | Map<AnySymbolView, IInputKey> inputKeys, | 31 | Map<AnySymbolView, IInputKey> inputKeys, |
32 | Map<AnyQuery, AnyQuery> canonicalQueryMap, | ||
30 | Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications, | 33 | Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications, |
31 | Set<AnyQuery> vacuousQueries) { | 34 | Set<AnyQuery> vacuousQueries) { |
32 | this.store = store; | 35 | this.store = store; |
33 | this.engineOptions = engineOptions; | 36 | this.engineOptions = engineOptions; |
34 | this.inputKeys = inputKeys; | 37 | this.inputKeys = inputKeys; |
38 | this.canonicalQueryMap = canonicalQueryMap; | ||
35 | this.querySpecifications = querySpecifications; | 39 | this.querySpecifications = querySpecifications; |
36 | this.vacuousQueries = vacuousQueries; | 40 | this.vacuousQueries = vacuousQueries; |
37 | var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size()); | 41 | var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size()); |
@@ -58,6 +62,17 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd | |||
58 | return allQueries; | 62 | return allQueries; |
59 | } | 63 | } |
60 | 64 | ||
65 | @Override | ||
66 | public <T> Query<T> getCanonicalQuery(Query<T> query) { | ||
67 | // We know that canonical forms of queries do not change output types. | ||
68 | @SuppressWarnings("unchecked") | ||
69 | var canonicalQuery = (Query<T>) canonicalQueryMap.get(query); | ||
70 | if (canonicalQuery == null) { | ||
71 | throw new IllegalArgumentException("Unknown query: " + query); | ||
72 | } | ||
73 | return canonicalQuery; | ||
74 | } | ||
75 | |||
61 | Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() { | 76 | Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() { |
62 | return querySpecifications; | 77 | return querySpecifications; |
63 | } | 78 | } |
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 d51bc9fc..8110a98f 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 | |||
@@ -36,14 +36,12 @@ import tools.refinery.store.util.CycleDetectingMapper; | |||
36 | 36 | ||
37 | import java.util.*; | 37 | import java.util.*; |
38 | import java.util.function.Function; | 38 | import java.util.function.Function; |
39 | import java.util.stream.Collectors; | ||
40 | 39 | ||
41 | public class Dnf2PQuery { | 40 | public class Dnf2PQuery { |
42 | private static final Object P_CONSTRAINT_LOCK = new Object(); | 41 | private static final Object P_CONSTRAINT_LOCK = new Object(); |
43 | private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name, | 42 | private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name, |
44 | this::doTranslate); | 43 | this::doTranslate); |
45 | private final QueryWrapperFactory wrapperFactory = new QueryWrapperFactory(this); | 44 | private final QueryWrapperFactory wrapperFactory = new QueryWrapperFactory(this); |
46 | private final Map<Dnf, QueryEvaluationHint> hintOverrides = new LinkedHashMap<>(); | ||
47 | private Function<Dnf, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null, | 45 | private Function<Dnf, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null, |
48 | (IQueryBackendFactory) null); | 46 | (IQueryBackendFactory) null); |
49 | 47 | ||
@@ -59,30 +57,9 @@ public class Dnf2PQuery { | |||
59 | return wrapperFactory.getSymbolViews(); | 57 | return wrapperFactory.getSymbolViews(); |
60 | } | 58 | } |
61 | 59 | ||
62 | public void hint(Dnf dnf, QueryEvaluationHint hint) { | ||
63 | hintOverrides.compute(dnf, (ignoredKey, existingHint) -> | ||
64 | existingHint == null ? hint : existingHint.overrideBy(hint)); | ||
65 | } | ||
66 | |||
67 | private QueryEvaluationHint consumeHint(Dnf dnf) { | ||
68 | var defaultHint = computeHint.apply(dnf); | ||
69 | var existingHint = hintOverrides.remove(dnf); | ||
70 | return defaultHint.overrideBy(existingHint); | ||
71 | } | ||
72 | |||
73 | public void assertNoUnusedHints() { | ||
74 | if (hintOverrides.isEmpty()) { | ||
75 | return; | ||
76 | } | ||
77 | var unusedHints = hintOverrides.keySet().stream().map(Dnf::name).collect(Collectors.joining(", ")); | ||
78 | throw new IllegalStateException( | ||
79 | "Unused query evaluation hints for %s. Hints must be set before a query is added to the engine" | ||
80 | .formatted(unusedHints)); | ||
81 | } | ||
82 | |||
83 | private RawPQuery doTranslate(Dnf dnfQuery) { | 60 | private RawPQuery doTranslate(Dnf dnfQuery) { |
84 | var pQuery = new RawPQuery(dnfQuery.getUniqueName()); | 61 | var pQuery = new RawPQuery(dnfQuery.getUniqueName()); |
85 | pQuery.setEvaluationHints(consumeHint(dnfQuery)); | 62 | pQuery.setEvaluationHints(computeHint.apply(dnfQuery)); |
86 | 63 | ||
87 | Map<SymbolicParameter, PParameter> parameters = new HashMap<>(); | 64 | Map<SymbolicParameter, PParameter> parameters = new HashMap<>(); |
88 | List<PParameter> parameterList = new ArrayList<>(); | 65 | List<PParameter> parameterList = new ArrayList<>(); |
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 index 6aae2ebe..3d2d5f83 100644 --- 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 | |||
@@ -98,6 +98,7 @@ class DiagonalQueryTest { | |||
98 | .build(); | 98 | .build(); |
99 | 99 | ||
100 | var model = store.createEmptyModel(); | 100 | var model = store.createEmptyModel(); |
101 | |||
101 | var personInterpretation = model.getInterpretation(person); | 102 | var personInterpretation = model.getInterpretation(person); |
102 | var symbolInterpretation = model.getInterpretation(symbol); | 103 | var symbolInterpretation = model.getInterpretation(symbol); |
103 | var queryEngine = model.getAdapter(ModelQueryAdapter.class); | 104 | var queryEngine = model.getAdapter(ModelQueryAdapter.class); |
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 be05b5e6..8ecbe11a 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 | |||
@@ -9,7 +9,9 @@ import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; | |||
9 | import org.junit.jupiter.api.Test; | 9 | import org.junit.jupiter.api.Test; |
10 | import tools.refinery.store.model.ModelStore; | 10 | import tools.refinery.store.model.ModelStore; |
11 | import tools.refinery.store.query.ModelQueryAdapter; | 11 | import tools.refinery.store.query.ModelQueryAdapter; |
12 | import tools.refinery.store.query.dnf.Dnf; | ||
12 | import tools.refinery.store.query.dnf.Query; | 13 | import tools.refinery.store.query.dnf.Query; |
14 | import tools.refinery.store.query.term.ParameterDirection; | ||
13 | import tools.refinery.store.query.term.Variable; | 15 | import tools.refinery.store.query.term.Variable; |
14 | import tools.refinery.store.query.viatra.tests.QueryEngineTest; | 16 | import tools.refinery.store.query.viatra.tests.QueryEngineTest; |
15 | import tools.refinery.store.query.view.AnySymbolView; | 17 | import tools.refinery.store.query.view.AnySymbolView; |
@@ -312,6 +314,53 @@ class QueryTest { | |||
312 | } | 314 | } |
313 | 315 | ||
314 | @QueryEngineTest | 316 | @QueryEngineTest |
317 | void patternCallInputArgumentTest(QueryEvaluationHint hint) { | ||
318 | var friendPredicate = Dnf.of("Friend", builder -> { | ||
319 | var p1 = builder.parameter("p1", ParameterDirection.IN); | ||
320 | var p2 = builder.parameter("p2", ParameterDirection.IN); | ||
321 | builder.clause( | ||
322 | personView.call(p1), | ||
323 | personView.call(p2), | ||
324 | friendMustView.call(p1, p2) | ||
325 | ); | ||
326 | }); | ||
327 | var predicate = Query.of("PositivePatternCall", (builder, p3, p4) -> builder.clause( | ||
328 | personView.call(p3), | ||
329 | personView.call(p4), | ||
330 | friendPredicate.call(p3, p4) | ||
331 | )); | ||
332 | |||
333 | var store = ModelStore.builder() | ||
334 | .symbols(person, friend) | ||
335 | .with(ViatraModelQueryAdapter.builder() | ||
336 | .defaultHint(hint) | ||
337 | .queries(predicate)) | ||
338 | .build(); | ||
339 | |||
340 | var model = store.createEmptyModel(); | ||
341 | var personInterpretation = model.getInterpretation(person); | ||
342 | var friendInterpretation = model.getInterpretation(friend); | ||
343 | var queryEngine = model.getAdapter(ModelQueryAdapter.class); | ||
344 | var predicateResultSet = queryEngine.getResultSet(predicate); | ||
345 | |||
346 | personInterpretation.put(Tuple.of(0), true); | ||
347 | personInterpretation.put(Tuple.of(1), true); | ||
348 | personInterpretation.put(Tuple.of(2), true); | ||
349 | |||
350 | friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE); | ||
351 | friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE); | ||
352 | friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); | ||
353 | |||
354 | queryEngine.flushChanges(); | ||
355 | assertResults(Map.of( | ||
356 | Tuple.of(0, 1), true, | ||
357 | Tuple.of(1, 0), true, | ||
358 | Tuple.of(1, 2), true, | ||
359 | Tuple.of(2, 1), false | ||
360 | ), predicateResultSet); | ||
361 | } | ||
362 | |||
363 | @QueryEngineTest | ||
315 | void negativeRelationViewTest(QueryEvaluationHint hint) { | 364 | void negativeRelationViewTest(QueryEvaluationHint hint) { |
316 | var predicate = Query.of("NegativePatternCall", (builder, p1, p2) -> builder.clause( | 365 | var predicate = Query.of("NegativePatternCall", (builder, p1, p2) -> builder.clause( |
317 | personView.call(p1), | 366 | personView.call(p1), |
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 index ca089a9d..5c1c4fc1 100644 --- 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 | |||
@@ -30,7 +30,7 @@ public final class QueryAssertions { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) { | 32 | public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) { |
33 | var defaultValue = resultSet.getQuery().defaultValue(); | 33 | var defaultValue = resultSet.getCanonicalQuery().defaultValue(); |
34 | var filteredExpected = new LinkedHashMap<Tuple, T>(); | 34 | var filteredExpected = new LinkedHashMap<Tuple, T>(); |
35 | var executables = new ArrayList<Executable>(); | 35 | var executables = new ArrayList<Executable>(); |
36 | for (var entry : expected.entrySet()) { | 36 | for (var entry : expected.entrySet()) { |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java index c62a95b5..332e6381 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java | |||
@@ -8,6 +8,7 @@ package tools.refinery.store.query; | |||
8 | import tools.refinery.store.adapter.ModelAdapterBuilder; | 8 | import tools.refinery.store.adapter.ModelAdapterBuilder; |
9 | import tools.refinery.store.model.ModelStore; | 9 | import tools.refinery.store.model.ModelStore; |
10 | import tools.refinery.store.query.dnf.AnyQuery; | 10 | import tools.refinery.store.query.dnf.AnyQuery; |
11 | import tools.refinery.store.query.rewriter.DnfRewriter; | ||
11 | 12 | ||
12 | import java.util.Collection; | 13 | import java.util.Collection; |
13 | import java.util.List; | 14 | import java.util.List; |
@@ -25,6 +26,8 @@ public interface ModelQueryBuilder extends ModelAdapterBuilder { | |||
25 | 26 | ||
26 | ModelQueryBuilder query(AnyQuery query); | 27 | ModelQueryBuilder query(AnyQuery query); |
27 | 28 | ||
29 | ModelQueryBuilder rewriter(DnfRewriter rewriter); | ||
30 | |||
28 | @Override | 31 | @Override |
29 | ModelQueryStoreAdapter build(ModelStore store); | 32 | ModelQueryStoreAdapter build(ModelStore store); |
30 | } | 33 | } |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java index f0a950a6..8b67c5c1 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java | |||
@@ -8,6 +8,7 @@ package tools.refinery.store.query; | |||
8 | import tools.refinery.store.adapter.ModelStoreAdapter; | 8 | import tools.refinery.store.adapter.ModelStoreAdapter; |
9 | import tools.refinery.store.model.Model; | 9 | import tools.refinery.store.model.Model; |
10 | import tools.refinery.store.query.dnf.AnyQuery; | 10 | import tools.refinery.store.query.dnf.AnyQuery; |
11 | import tools.refinery.store.query.dnf.Query; | ||
11 | import tools.refinery.store.query.view.AnySymbolView; | 12 | import tools.refinery.store.query.view.AnySymbolView; |
12 | 13 | ||
13 | import java.util.Collection; | 14 | import java.util.Collection; |
@@ -17,6 +18,12 @@ public interface ModelQueryStoreAdapter extends ModelStoreAdapter { | |||
17 | 18 | ||
18 | Collection<AnyQuery> getQueries(); | 19 | Collection<AnyQuery> getQueries(); |
19 | 20 | ||
21 | default AnyQuery getCanonicalQuery(AnyQuery query) { | ||
22 | return getCanonicalQuery((Query<?>) query); | ||
23 | } | ||
24 | |||
25 | <T> Query<T> getCanonicalQuery(Query<T> query); | ||
26 | |||
20 | @Override | 27 | @Override |
21 | ModelQueryAdapter createModelAdapter(Model model); | 28 | ModelQueryAdapter createModelAdapter(Model model); |
22 | } | 29 | } |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java index 55f748da..83fe6ccd 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java | |||
@@ -44,6 +44,9 @@ public abstract sealed class Query<T> implements AnyQuery permits FunctionalQuer | |||
44 | public abstract T defaultValue(); | 44 | public abstract T defaultValue(); |
45 | 45 | ||
46 | public Query<T> withDnf(Dnf newDnf) { | 46 | public Query<T> withDnf(Dnf newDnf) { |
47 | if (dnf.equals(newDnf)) { | ||
48 | return this; | ||
49 | } | ||
47 | int arity = dnf.arity(); | 50 | int arity = dnf.arity(); |
48 | if (newDnf.arity() != arity) { | 51 | if (newDnf.arity() != arity) { |
49 | throw new IllegalArgumentException("Arity of %s and %s do not match".formatted(dnf, newDnf)); | 52 | throw new IllegalArgumentException("Arity of %s and %s do not match".formatted(dnf, newDnf)); |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AbstractResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AbstractResultSet.java index a710c64d..dcfe6cc5 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AbstractResultSet.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AbstractResultSet.java | |||
@@ -28,7 +28,7 @@ public abstract class AbstractResultSet<T> implements ResultSet<T> { | |||
28 | } | 28 | } |
29 | 29 | ||
30 | @Override | 30 | @Override |
31 | public Query<T> getQuery() { | 31 | public Query<T> getCanonicalQuery() { |
32 | return query; | 32 | return query; |
33 | } | 33 | } |
34 | 34 | ||
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AnyResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AnyResultSet.java index 02809477..5b75b103 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AnyResultSet.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/AnyResultSet.java | |||
@@ -11,7 +11,7 @@ import tools.refinery.store.query.dnf.AnyQuery; | |||
11 | public sealed interface AnyResultSet permits ResultSet { | 11 | public sealed interface AnyResultSet permits ResultSet { |
12 | ModelQueryAdapter getAdapter(); | 12 | ModelQueryAdapter getAdapter(); |
13 | 13 | ||
14 | AnyQuery getQuery(); | 14 | AnyQuery getCanonicalQuery(); |
15 | 15 | ||
16 | int size(); | 16 | int size(); |
17 | } | 17 | } |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/EmptyResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/EmptyResultSet.java index 2795a44b..991b1e32 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/EmptyResultSet.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/EmptyResultSet.java | |||
@@ -18,7 +18,7 @@ public record EmptyResultSet<T>(ModelQueryAdapter adapter, Query<T> query) imple | |||
18 | } | 18 | } |
19 | 19 | ||
20 | @Override | 20 | @Override |
21 | public Query<T> getQuery() { | 21 | public Query<T> getCanonicalQuery() { |
22 | return query; | 22 | return query; |
23 | } | 23 | } |
24 | 24 | ||
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/OrderedResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/OrderedResultSet.java index 39006d65..df12b967 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/OrderedResultSet.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/OrderedResultSet.java | |||
@@ -17,7 +17,7 @@ public class OrderedResultSet<T> implements AutoCloseable, ResultSet<T> { | |||
17 | private final ResultSet<T> resultSet; | 17 | private final ResultSet<T> resultSet; |
18 | private final OrderStatisticTree<Tuple> tree = new OrderStatisticTree<>(); | 18 | private final OrderStatisticTree<Tuple> tree = new OrderStatisticTree<>(); |
19 | private final ResultSetListener<T> listener = (key, fromValue, toValue) -> { | 19 | private final ResultSetListener<T> listener = (key, fromValue, toValue) -> { |
20 | var defaultValue = getQuery().defaultValue(); | 20 | var defaultValue = getCanonicalQuery().defaultValue(); |
21 | if (Objects.equals(defaultValue, toValue)) { | 21 | if (Objects.equals(defaultValue, toValue)) { |
22 | tree.remove(key); | 22 | tree.remove(key); |
23 | } else { | 23 | } else { |
@@ -45,8 +45,8 @@ public class OrderedResultSet<T> implements AutoCloseable, ResultSet<T> { | |||
45 | } | 45 | } |
46 | 46 | ||
47 | @Override | 47 | @Override |
48 | public Query<T> getQuery() { | 48 | public Query<T> getCanonicalQuery() { |
49 | return resultSet.getQuery(); | 49 | return resultSet.getCanonicalQuery(); |
50 | } | 50 | } |
51 | 51 | ||
52 | @Override | 52 | @Override |
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/ResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/ResultSet.java index 33d1ea95..a6e99784 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/ResultSet.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/resultset/ResultSet.java | |||
@@ -10,7 +10,7 @@ import tools.refinery.store.query.dnf.Query; | |||
10 | import tools.refinery.store.tuple.Tuple; | 10 | import tools.refinery.store.tuple.Tuple; |
11 | 11 | ||
12 | public non-sealed interface ResultSet<T> extends AnyResultSet { | 12 | public non-sealed interface ResultSet<T> extends AnyResultSet { |
13 | Query<T> getQuery(); | 13 | Query<T> getCanonicalQuery(); |
14 | 14 | ||
15 | T get(Tuple parameters); | 15 | T get(Tuple parameters); |
16 | 16 | ||
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/rewriter/ClauseInputParameterResolver.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/rewriter/ClauseInputParameterResolver.java index bdd07f19..aa06a05a 100644 --- a/subprojects/store-query/src/main/java/tools/refinery/store/query/rewriter/ClauseInputParameterResolver.java +++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/rewriter/ClauseInputParameterResolver.java | |||
@@ -53,7 +53,7 @@ class ClauseInputParameterResolver { | |||
53 | } | 53 | } |
54 | boolean hasInputParameter = hasInputParameter(targetDnf); | 54 | boolean hasInputParameter = hasInputParameter(targetDnf); |
55 | if (!hasInputParameter) { | 55 | if (!hasInputParameter) { |
56 | targetDnf = rewriter.doRewrite(targetDnf); | 56 | targetDnf = rewriter.rewrite(targetDnf); |
57 | } | 57 | } |
58 | if (inlinePositiveClause(abstractCallLiteral, targetDnf)) { | 58 | if (inlinePositiveClause(abstractCallLiteral, targetDnf)) { |
59 | return; | 59 | return; |
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java index ddb2a069..ef0077e4 100644 --- a/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java +++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/rewriter/InputParameterResolverTest.java | |||
@@ -18,6 +18,7 @@ import tools.refinery.store.representation.Symbol; | |||
18 | import java.util.List; | 18 | import java.util.List; |
19 | 19 | ||
20 | import static org.hamcrest.MatcherAssert.assertThat; | 20 | import static org.hamcrest.MatcherAssert.assertThat; |
21 | import static org.hamcrest.Matchers.is; | ||
21 | import static tools.refinery.store.query.literal.Literals.not; | 22 | import static tools.refinery.store.query.literal.Literals.not; |
22 | import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo; | 23 | import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo; |
23 | 24 | ||
@@ -205,4 +206,23 @@ class InputParameterResolverTest { | |||
205 | 206 | ||
206 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); | 207 | assertThat(actual.getDnf(), structurallyEqualTo(expected.getDnf())); |
207 | } | 208 | } |
209 | |||
210 | @Test | ||
211 | void identityWhenNoWorkToDoTest() { | ||
212 | var dnf = Dnf.of("SubQuery", builder -> { | ||
213 | var x = builder.parameter("x", ParameterDirection.OUT); | ||
214 | builder.clause( | ||
215 | personView.call(x), | ||
216 | not(friendView.call(x, Variable.of())) | ||
217 | ); | ||
218 | }); | ||
219 | var query = Query.of("Actual", (builder, p1) -> builder.clause( | ||
220 | personView.call(p1), | ||
221 | not(dnf.call(p1)) | ||
222 | )); | ||
223 | |||
224 | var actual = sut.rewrite(query); | ||
225 | |||
226 | assertThat(actual, is(query)); | ||
227 | } | ||
208 | } | 228 | } |