From 8d2d6359f3b47b36075ea5aad8b522c094f5a71c Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 9 Feb 2023 16:52:43 +0100 Subject: refactor: Atom -> Literal naming convention --- .../query/viatra/ViatraModelQueryBuilder.java | 14 +- .../internal/ViatraModelQueryAdapterImpl.java | 6 +- .../internal/ViatraModelQueryBuilderImpl.java | 16 +- .../internal/ViatraModelQueryStoreAdapterImpl.java | 10 +- .../query/viatra/internal/pquery/DNF2PQuery.java | 223 --------------------- .../query/viatra/internal/pquery/Dnf2PQuery.java | 223 +++++++++++++++++++++ .../refinery/store/query/viatra/QueryTest.java | 155 +++++++------- .../store/query/viatra/QueryTransactionTest.java | 8 +- 8 files changed, 329 insertions(+), 326 deletions(-) delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java (limited to 'subprojects/store-query-viatra/src') diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java index efc6146c..24aa52e2 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,7 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.DNF; +import tools.refinery.store.query.Dnf; import tools.refinery.store.query.ModelQueryBuilder; import java.util.Collection; @@ -23,25 +23,25 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder { ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); @Override - default ViatraModelQueryBuilder queries(DNF... queries) { + default ViatraModelQueryBuilder queries(Dnf... queries) { ModelQueryBuilder.super.queries(queries); return this; } @Override - default ViatraModelQueryBuilder queries(Collection queries) { + default ViatraModelQueryBuilder queries(Collection queries) { ModelQueryBuilder.super.queries(queries); return this; } @Override - ViatraModelQueryBuilder query(DNF query); + ViatraModelQueryBuilder query(Dnf query); - ViatraModelQueryBuilder query(DNF query, QueryEvaluationHint queryEvaluationHint); + ViatraModelQueryBuilder query(Dnf query, QueryEvaluationHint queryEvaluationHint); - ViatraModelQueryBuilder computeHint(Function computeHint); + ViatraModelQueryBuilder computeHint(Function computeHint); - ViatraModelQueryBuilder hint(DNF dnf, QueryEvaluationHint queryEvaluationHint); + ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint); @Override ViatraModelQueryStoreAdapter createStoreAdapter(ModelStore store); diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java index 039f46fa..e5d8e2f6 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,7 +7,7 @@ import org.eclipse.viatra.query.runtime.internal.apiimpl.ViatraQueryEngineImpl; import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend; import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; import tools.refinery.store.model.Model; -import tools.refinery.store.query.DNF; +import tools.refinery.store.query.Dnf; import tools.refinery.store.query.ResultSet; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; @@ -27,7 +27,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { private final ViatraQueryEngineImpl queryEngine; private final MethodHandle setUpdatePropagationDelayedHandle; private final MethodHandle getQueryBackendsHandle; - private final Map resultSets; + private final Map resultSets; private boolean pendingChanges; ViatraModelQueryAdapterImpl(Model model, ViatraModelQueryStoreAdapterImpl storeAdapter) { @@ -95,7 +95,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { } @Override - public ResultSet getResultSet(DNF query) { + public ResultSet getResultSet(Dnf query) { var resultSet = resultSets.get(query); if (resultSet == null) { throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name())); diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java index 9f1e55b1..af20033a 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 @@ -9,9 +9,9 @@ import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.DNF; +import tools.refinery.store.query.Dnf; import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; -import tools.refinery.store.query.viatra.internal.pquery.DNF2PQuery; +import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher; import java.util.Collections; @@ -21,8 +21,8 @@ import java.util.function.Function; public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder { private ViatraQueryEngineOptions.Builder engineOptionsBuilder; - private final DNF2PQuery dnf2PQuery = new DNF2PQuery(); - private final Map> querySpecifications = new LinkedHashMap<>(); + private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); + private final Map> querySpecifications = new LinkedHashMap<>(); public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) { super(storeBuilder); @@ -63,7 +63,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp } @Override - public ViatraModelQueryBuilder query(DNF query) { + public ViatraModelQueryBuilder query(Dnf query) { if (querySpecifications.containsKey(query)) { throw new IllegalArgumentException("%s was already added to the query engine".formatted(query.name())); } @@ -73,20 +73,20 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp } @Override - public ViatraModelQueryBuilder query(DNF query, QueryEvaluationHint queryEvaluationHint) { + public ViatraModelQueryBuilder query(Dnf query, QueryEvaluationHint queryEvaluationHint) { query(query); hint(query, queryEvaluationHint); return this; } @Override - public ViatraModelQueryBuilder computeHint(Function computeHint) { + public ViatraModelQueryBuilder computeHint(Function computeHint) { dnf2PQuery.setComputeHint(computeHint); return this; } @Override - public ViatraModelQueryBuilder hint(DNF dnf, QueryEvaluationHint queryEvaluationHint) { + public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) { var pQuery = dnf2PQuery.getAlreadyTranslated(dnf); if (pQuery == null) { throw new IllegalArgumentException( diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java index 394e407e..8323118b 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,7 +5,7 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.DNF; +import tools.refinery.store.query.Dnf; import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter; import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher; import tools.refinery.store.query.view.AnyRelationView; @@ -17,11 +17,11 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd private final ModelStore store; private final ViatraQueryEngineOptions engineOptions; private final Map inputKeys; - private final Map> querySpecifications; + private final Map> querySpecifications; ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, Map inputKeys, - Map> querySpecifications) { + Map> querySpecifications) { this.store = store; this.engineOptions = engineOptions; this.inputKeys = inputKeys; @@ -42,11 +42,11 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd } @Override - public Collection getQueries() { + public Collection getQueries() { return querySpecifications.keySet(); } - Map> getQuerySpecifications() { + Map> getQuerySpecifications() { return querySpecifications; } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java deleted file mode 100644 index 60f1bcae..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/DNF2PQuery.java +++ /dev/null @@ -1,223 +0,0 @@ -package tools.refinery.store.query.viatra.internal.pquery; - -import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; -import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; -import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; -import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; -import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; -import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; -import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; -import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; -import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; -import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; -import tools.refinery.store.query.DNF; -import tools.refinery.store.query.DNFAnd; -import tools.refinery.store.query.DNFUtils; -import tools.refinery.store.query.Variable; -import tools.refinery.store.query.atom.*; -import tools.refinery.store.query.view.AnyRelationView; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class DNF2PQuery { - private static final Object P_CONSTRAINT_LOCK = new Object(); - - private final Set translating = new LinkedHashSet<>(); - - private final Map dnf2PQueryMap = new HashMap<>(); - - private final Map view2WrapperMap = new LinkedHashMap<>(); - - private final Map view2EmbeddedMap = new HashMap<>(); - - private Function computeHint = dnf -> new QueryEvaluationHint(null, - QueryEvaluationHint.BackendRequirement.UNSPECIFIED); - - public void setComputeHint(Function computeHint) { - this.computeHint = computeHint; - } - - public RawPQuery translate(DNF dnfQuery) { - if (translating.contains(dnfQuery)) { - var path = translating.stream().map(DNF::name).collect(Collectors.joining(" -> ")); - throw new IllegalStateException("Circular reference %s -> %s detected".formatted(path, - dnfQuery.name())); - } - // We can't use computeIfAbsent here, because translating referenced queries calls this method in a reentrant - // way, which would cause a ConcurrentModificationException with computeIfAbsent. - var pQuery = dnf2PQueryMap.get(dnfQuery); - if (pQuery == null) { - translating.add(dnfQuery); - try { - pQuery = doTranslate(dnfQuery); - dnf2PQueryMap.put(dnfQuery, pQuery); - } finally { - translating.remove(dnfQuery); - } - } - return pQuery; - } - - public Map getRelationViews() { - return Collections.unmodifiableMap(view2WrapperMap); - } - - public RawPQuery getAlreadyTranslated(DNF dnfQuery) { - return dnf2PQueryMap.get(dnfQuery); - } - - private RawPQuery doTranslate(DNF dnfQuery) { - var pQuery = new RawPQuery(dnfQuery.getUniqueName()); - pQuery.setEvaluationHints(computeHint.apply(dnfQuery)); - - Map parameters = new HashMap<>(); - for (Variable variable : dnfQuery.getParameters()) { - parameters.put(variable, new PParameter(variable.getUniqueName())); - } - - List parameterList = new ArrayList<>(); - for (var param : dnfQuery.getParameters()) { - parameterList.add(parameters.get(param)); - } - pQuery.setParameters(parameterList); - - for (var functionalDependency : dnfQuery.getFunctionalDependencies()) { - var functionalDependencyAnnotation = new PAnnotation("FunctionalDependency"); - for (var forEachVariable : functionalDependency.forEach()) { - functionalDependencyAnnotation.addAttribute("forEach", forEachVariable.getUniqueName()); - } - for (var uniqueVariable : functionalDependency.unique()) { - functionalDependencyAnnotation.addAttribute("unique", uniqueVariable.getUniqueName()); - } - pQuery.addAnnotation(functionalDependencyAnnotation); - } - - // The constructor of {@link org.eclipse.viatra.query.runtime.matchers.psystem.BasePConstraint} mutates - // global static state (nextID) without locking. Therefore, we need to synchronize before creating - // any query constraints to avoid a data race. - synchronized (P_CONSTRAINT_LOCK) { - for (DNFAnd clause : dnfQuery.getClauses()) { - PBody body = new PBody(pQuery); - List symbolicParameters = new ArrayList<>(); - for (var param : dnfQuery.getParameters()) { - PVariable pVar = body.getOrCreateVariableByName(param.getUniqueName()); - symbolicParameters.add(new ExportedParameter(body, pVar, parameters.get(param))); - } - body.setSymbolicParameters(symbolicParameters); - pQuery.addBody(body); - for (DNFAtom constraint : clause.constraints()) { - translateDNFAtom(constraint, body); - } - } - } - - return pQuery; - } - - private void translateDNFAtom(DNFAtom constraint, PBody body) { - if (constraint instanceof EquivalenceAtom equivalenceAtom) { - translateEquivalenceAtom(equivalenceAtom, body); - } else if (constraint instanceof RelationViewAtom relationViewAtom) { - translateRelationViewAtom(relationViewAtom, body); - } else if (constraint instanceof DNFCallAtom callAtom) { - translateCallAtom(callAtom, body); - } else if (constraint instanceof ConstantAtom constantAtom) { - translateConstantAtom(constantAtom, body); - } else { - throw new IllegalArgumentException("Unknown constraint: " + constraint.toString()); - } - } - - private void translateEquivalenceAtom(EquivalenceAtom equivalence, PBody body) { - PVariable varSource = body.getOrCreateVariableByName(equivalence.left().getUniqueName()); - PVariable varTarget = body.getOrCreateVariableByName(equivalence.right().getUniqueName()); - if (equivalence.positive()) { - new Equality(body, varSource, varTarget); - } else { - new Inequality(body, varSource, varTarget); - } - } - - private void translateRelationViewAtom(RelationViewAtom relationViewAtom, PBody body) { - var substitution = translateSubstitution(relationViewAtom.getSubstitution(), body); - var polarity = relationViewAtom.getPolarity(); - var relationView = relationViewAtom.getTarget(); - if (polarity == CallPolarity.POSITIVE) { - new TypeConstraint(body, substitution, wrapView(relationView)); - } else { - var embeddedPQuery = translateEmbeddedRelationViewPQuery(relationView); - switch (polarity) { - case TRANSITIVE -> new BinaryTransitiveClosure(body, substitution, embeddedPQuery); - case NEGATIVE -> new NegativePatternCall(body, substitution, embeddedPQuery); - default -> throw new IllegalArgumentException("Unknown polarity: " + polarity); - } - } - } - - private static Tuple translateSubstitution(List substitution, PBody body) { - int arity = substitution.size(); - Object[] variables = new Object[arity]; - for (int i = 0; i < arity; i++) { - var variable = substitution.get(i); - variables[i] = body.getOrCreateVariableByName(variable.getUniqueName()); - } - return Tuples.flatTupleOf(variables); - } - - private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) { - return view2EmbeddedMap.computeIfAbsent(relationView, this::doTranslateEmbeddedRelationViewPQuery); - } - - private RawPQuery doTranslateEmbeddedRelationViewPQuery(AnyRelationView relationView) { - var embeddedPQuery = new RawPQuery(DNFUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED); - var body = new PBody(embeddedPQuery); - int arity = relationView.arity(); - var parameters = new ArrayList(arity); - var arguments = new Object[arity]; - var symbolicParameters = new ArrayList(arity); - for (int i = 0; i < arity; i++) { - var parameterName = "p" + i; - var parameter = new PParameter(parameterName); - parameters.add(parameter); - var variable = body.getOrCreateVariableByName(parameterName); - arguments[i] = variable; - symbolicParameters.add(new ExportedParameter(body, variable, parameter)); - } - embeddedPQuery.setParameters(parameters); - body.setSymbolicParameters(symbolicParameters); - var argumentTuple = Tuples.flatTupleOf(arguments); - new TypeConstraint(body, argumentTuple, wrapView(relationView)); - embeddedPQuery.addBody(body); - return embeddedPQuery; - } - - private RelationViewWrapper wrapView(AnyRelationView relationView) { - return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new); - } - - private void translateCallAtom(DNFCallAtom callAtom, PBody body) { - var variablesTuple = translateSubstitution(callAtom.getSubstitution(), body); - var translatedReferred = translate(callAtom.getTarget()); - var polarity = callAtom.getPolarity(); - switch (polarity) { - case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred); - case TRANSITIVE -> new BinaryTransitiveClosure(body, variablesTuple, translatedReferred); - case NEGATIVE -> new NegativePatternCall(body, variablesTuple, translatedReferred); - default -> throw new IllegalArgumentException("Unknown polarity: " + polarity); - } - } - - private void translateConstantAtom(ConstantAtom constantAtom, PBody body) { - var variable = body.getOrCreateVariableByName(constantAtom.variable().getUniqueName()); - new ConstantValue(body, variable, constantAtom.nodeId()); - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java new file mode 100644 index 00000000..02e26bde --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java @@ -0,0 +1,223 @@ +package tools.refinery.store.query.viatra.internal.pquery; + +import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; +import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; +import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; +import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; +import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; +import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; +import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; +import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; +import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; +import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; +import tools.refinery.store.query.Dnf; +import tools.refinery.store.query.DnfClause; +import tools.refinery.store.query.DnfUtils; +import tools.refinery.store.query.Variable; +import tools.refinery.store.query.literal.*; +import tools.refinery.store.query.view.AnyRelationView; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class Dnf2PQuery { + private static final Object P_CONSTRAINT_LOCK = new Object(); + + private final Set translating = new LinkedHashSet<>(); + + private final Map dnf2PQueryMap = new HashMap<>(); + + private final Map view2WrapperMap = new LinkedHashMap<>(); + + private final Map view2EmbeddedMap = new HashMap<>(); + + private Function computeHint = dnf -> new QueryEvaluationHint(null, + QueryEvaluationHint.BackendRequirement.UNSPECIFIED); + + public void setComputeHint(Function computeHint) { + this.computeHint = computeHint; + } + + public RawPQuery translate(Dnf dnfQuery) { + if (translating.contains(dnfQuery)) { + var path = translating.stream().map(Dnf::name).collect(Collectors.joining(" -> ")); + throw new IllegalStateException("Circular reference %s -> %s detected".formatted(path, + dnfQuery.name())); + } + // We can't use computeIfAbsent here, because translating referenced queries calls this method in a reentrant + // way, which would cause a ConcurrentModificationException with computeIfAbsent. + var pQuery = dnf2PQueryMap.get(dnfQuery); + if (pQuery == null) { + translating.add(dnfQuery); + try { + pQuery = doTranslate(dnfQuery); + dnf2PQueryMap.put(dnfQuery, pQuery); + } finally { + translating.remove(dnfQuery); + } + } + return pQuery; + } + + public Map getRelationViews() { + return Collections.unmodifiableMap(view2WrapperMap); + } + + public RawPQuery getAlreadyTranslated(Dnf dnfQuery) { + return dnf2PQueryMap.get(dnfQuery); + } + + private RawPQuery doTranslate(Dnf dnfQuery) { + var pQuery = new RawPQuery(dnfQuery.getUniqueName()); + pQuery.setEvaluationHints(computeHint.apply(dnfQuery)); + + Map parameters = new HashMap<>(); + for (Variable variable : dnfQuery.getParameters()) { + parameters.put(variable, new PParameter(variable.getUniqueName())); + } + + List parameterList = new ArrayList<>(); + for (var param : dnfQuery.getParameters()) { + parameterList.add(parameters.get(param)); + } + pQuery.setParameters(parameterList); + + for (var functionalDependency : dnfQuery.getFunctionalDependencies()) { + var functionalDependencyAnnotation = new PAnnotation("FunctionalDependency"); + for (var forEachVariable : functionalDependency.forEach()) { + functionalDependencyAnnotation.addAttribute("forEach", forEachVariable.getUniqueName()); + } + for (var uniqueVariable : functionalDependency.unique()) { + functionalDependencyAnnotation.addAttribute("unique", uniqueVariable.getUniqueName()); + } + pQuery.addAnnotation(functionalDependencyAnnotation); + } + + // The constructor of {@link org.eclipse.viatra.query.runtime.matchers.psystem.BasePConstraint} mutates + // global static state (nextID) without locking. Therefore, we need to synchronize before creating + // any query literals to avoid a data race. + synchronized (P_CONSTRAINT_LOCK) { + for (DnfClause clause : dnfQuery.getClauses()) { + PBody body = new PBody(pQuery); + List symbolicParameters = new ArrayList<>(); + for (var param : dnfQuery.getParameters()) { + PVariable pVar = body.getOrCreateVariableByName(param.getUniqueName()); + symbolicParameters.add(new ExportedParameter(body, pVar, parameters.get(param))); + } + body.setSymbolicParameters(symbolicParameters); + pQuery.addBody(body); + for (Literal literal : clause.literals()) { + translateLiteral(literal, body); + } + } + } + + return pQuery; + } + + private void translateLiteral(Literal literal, PBody body) { + if (literal instanceof EquivalenceLiteral equivalenceLiteral) { + translateEquivalenceLiteral(equivalenceLiteral, body); + } else if (literal instanceof RelationViewLiteral relationViewLiteral) { + translateRelationViewLiteral(relationViewLiteral, body); + } else if (literal instanceof DnfCallLiteral dnfCallLiteral) { + translateDnfCallLiteral(dnfCallLiteral, body); + } else if (literal instanceof ConstantLiteral constantLiteral) { + translateConstantLiteral(constantLiteral, body); + } else { + throw new IllegalArgumentException("Unknown literal: " + literal.toString()); + } + } + + private void translateEquivalenceLiteral(EquivalenceLiteral equivalenceLiteral, PBody body) { + PVariable varSource = body.getOrCreateVariableByName(equivalenceLiteral.left().getUniqueName()); + PVariable varTarget = body.getOrCreateVariableByName(equivalenceLiteral.right().getUniqueName()); + if (equivalenceLiteral.positive()) { + new Equality(body, varSource, varTarget); + } else { + new Inequality(body, varSource, varTarget); + } + } + + private void translateRelationViewLiteral(RelationViewLiteral relationViewLiteral, PBody body) { + var substitution = translateSubstitution(relationViewLiteral.getSubstitution(), body); + var polarity = relationViewLiteral.getPolarity(); + var relationView = relationViewLiteral.getTarget(); + if (polarity == CallPolarity.POSITIVE) { + new TypeConstraint(body, substitution, wrapView(relationView)); + } else { + var embeddedPQuery = translateEmbeddedRelationViewPQuery(relationView); + switch (polarity) { + case TRANSITIVE -> new BinaryTransitiveClosure(body, substitution, embeddedPQuery); + case NEGATIVE -> new NegativePatternCall(body, substitution, embeddedPQuery); + default -> throw new IllegalArgumentException("Unknown polarity: " + polarity); + } + } + } + + private static Tuple translateSubstitution(List substitution, PBody body) { + int arity = substitution.size(); + Object[] variables = new Object[arity]; + for (int i = 0; i < arity; i++) { + var variable = substitution.get(i); + variables[i] = body.getOrCreateVariableByName(variable.getUniqueName()); + } + return Tuples.flatTupleOf(variables); + } + + private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) { + return view2EmbeddedMap.computeIfAbsent(relationView, this::doTranslateEmbeddedRelationViewPQuery); + } + + private RawPQuery doTranslateEmbeddedRelationViewPQuery(AnyRelationView relationView) { + var embeddedPQuery = new RawPQuery(DnfUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED); + var body = new PBody(embeddedPQuery); + int arity = relationView.arity(); + var parameters = new ArrayList(arity); + var arguments = new Object[arity]; + var symbolicParameters = new ArrayList(arity); + for (int i = 0; i < arity; i++) { + var parameterName = "p" + i; + var parameter = new PParameter(parameterName); + parameters.add(parameter); + var variable = body.getOrCreateVariableByName(parameterName); + arguments[i] = variable; + symbolicParameters.add(new ExportedParameter(body, variable, parameter)); + } + embeddedPQuery.setParameters(parameters); + body.setSymbolicParameters(symbolicParameters); + var argumentTuple = Tuples.flatTupleOf(arguments); + new TypeConstraint(body, argumentTuple, wrapView(relationView)); + embeddedPQuery.addBody(body); + return embeddedPQuery; + } + + private RelationViewWrapper wrapView(AnyRelationView relationView) { + return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new); + } + + private void translateDnfCallLiteral(DnfCallLiteral dnfCallLiteral, PBody body) { + var variablesTuple = translateSubstitution(dnfCallLiteral.getSubstitution(), body); + var translatedReferred = translate(dnfCallLiteral.getTarget()); + var polarity = dnfCallLiteral.getPolarity(); + switch (polarity) { + case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred); + case TRANSITIVE -> new BinaryTransitiveClosure(body, variablesTuple, translatedReferred); + case NEGATIVE -> new NegativePatternCall(body, variablesTuple, translatedReferred); + default -> throw new IllegalArgumentException("Unknown polarity: " + polarity); + } + } + + private void translateConstantLiteral(ConstantLiteral constantLiteral, PBody body) { + var variable = body.getOrCreateVariableByName(constantLiteral.variable().getUniqueName()); + new ConstantValue(body, variable, constantLiteral.nodeId()); + } +} 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 6a37b54a..471fdfc9 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 @@ -2,10 +2,13 @@ package tools.refinery.store.query.viatra; import org.junit.jupiter.api.Test; import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.DNF; +import tools.refinery.store.query.Dnf; import tools.refinery.store.query.ModelQuery; import tools.refinery.store.query.Variable; -import tools.refinery.store.query.atom.*; +import tools.refinery.store.query.literal.CallPolarity; +import tools.refinery.store.query.literal.DnfCallLiteral; +import tools.refinery.store.query.literal.EquivalenceLiteral; +import tools.refinery.store.query.literal.RelationViewLiteral; import tools.refinery.store.query.view.FilteredRelationView; import tools.refinery.store.query.view.KeyOnlyRelationView; import tools.refinery.store.representation.Symbol; @@ -27,9 +30,9 @@ class QueryTest { var personView = new KeyOnlyRelationView<>(person); var p1 = new Variable("p1"); - var predicate = DNF.builder("TypeConstraint") + var predicate = Dnf.builder("TypeConstraint") .parameters(p1) - .clause(new RelationViewAtom(personView, p1)) + .clause(new RelationViewLiteral(personView, p1)) .build(); var store = ModelStore.builder() @@ -64,12 +67,12 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("RelationConstraint") + var predicate = Dnf.builder("RelationConstraint") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .build(); @@ -111,13 +114,13 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("RelationConstraint") + var predicate = Dnf.builder("RelationConstraint") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2), - new RelationViewAtom(friendMustView, p2, p1) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2), + new RelationViewLiteral(friendMustView, p2, p1) ) .build(); @@ -166,12 +169,12 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("RelationConstraint") + var predicate = Dnf.builder("RelationConstraint") .parameters(p1) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .build(); @@ -213,17 +216,17 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("Or") + var predicate = Dnf.builder("Or") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .clause( - new RelationViewAtom(animalView, p1), - new RelationViewAtom(animalView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(animalView, p1), + new RelationViewLiteral(animalView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .build(); @@ -263,12 +266,12 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("Equality") + var predicate = Dnf.builder("Equality") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new EquivalenceAtom(p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new EquivalenceLiteral(p1, p2) ) .build(); @@ -302,14 +305,14 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); var p3 = new Variable("p3"); - var predicate = DNF.builder("Inequality") + var predicate = Dnf.builder("Inequality") .parameters(p1, p2, p3) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p3), - new RelationViewAtom(friendMustView, p2, p3), - new EquivalenceAtom(false, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p3), + new RelationViewLiteral(friendMustView, p2, p3), + new EquivalenceLiteral(false, p1, p2) ) .build(); @@ -346,23 +349,23 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var friendPredicate = DNF.builder("RelationConstraint") + var friendPredicate = Dnf.builder("RelationConstraint") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .build(); var p3 = new Variable("p3"); var p4 = new Variable("p4"); - var predicate = DNF.builder("PositivePatternCall") + var predicate = Dnf.builder("PositivePatternCall") .parameters(p3, p4) .clause( - new RelationViewAtom(personView, p3), - new RelationViewAtom(personView, p4), - new DNFCallAtom(friendPredicate, p3, p4) + new RelationViewLiteral(personView, p3), + new RelationViewLiteral(personView, p4), + new DnfCallLiteral(friendPredicate, p3, p4) ) .build(); @@ -399,12 +402,12 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("NegativePatternCall") + var predicate = Dnf.builder("NegativePatternCall") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(false, friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(false, friendMustView, p1, p2) ) .build(); @@ -441,23 +444,23 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var friendPredicate = DNF.builder("RelationConstraint") + var friendPredicate = Dnf.builder("RelationConstraint") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .build(); var p3 = new Variable("p3"); var p4 = new Variable("p4"); - var predicate = DNF.builder("NegativePatternCall") + var predicate = Dnf.builder("NegativePatternCall") .parameters(p3, p4) .clause( - new RelationViewAtom(personView, p3), - new RelationViewAtom(personView, p4), - new DNFCallAtom(false, friendPredicate, p3, p4) + new RelationViewLiteral(personView, p3), + new RelationViewLiteral(personView, p4), + new DnfCallLiteral(false, friendPredicate, p3, p4) ) .build(); @@ -495,11 +498,11 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("Count") + var predicate = Dnf.builder("Count") .parameters(p1) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(false, friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(false, friendMustView, p1, p2) ) .build(); @@ -536,20 +539,20 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var called = DNF.builder("Called") + var called = Dnf.builder("Called") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .build(); - var predicate = DNF.builder("Count") + var predicate = Dnf.builder("Count") .parameters(p1) .clause( - new RelationViewAtom(personView, p1), - new DNFCallAtom(false, called, p1, p2) + new RelationViewLiteral(personView, p1), + new DnfCallLiteral(false, called, p1, p2) ) .build(); @@ -585,12 +588,12 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var predicate = DNF.builder("TransitivePatternCall") + var predicate = Dnf.builder("TransitivePatternCall") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(CallPolarity.TRANSITIVE, friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(CallPolarity.TRANSITIVE, friendMustView, p1, p2) ) .build(); @@ -626,23 +629,23 @@ class QueryTest { var p1 = new Variable("p1"); var p2 = new Variable("p2"); - var friendPredicate = DNF.builder("RelationConstraint") + var friendPredicate = Dnf.builder("RelationConstraint") .parameters(p1, p2) .clause( - new RelationViewAtom(personView, p1), - new RelationViewAtom(personView, p2), - new RelationViewAtom(friendMustView, p1, p2) + new RelationViewLiteral(personView, p1), + new RelationViewLiteral(personView, p2), + new RelationViewLiteral(friendMustView, p1, p2) ) .build(); var p3 = new Variable("p3"); var p4 = new Variable("p4"); - var predicate = DNF.builder("TransitivePatternCall") + var predicate = Dnf.builder("TransitivePatternCall") .parameters(p3, p4) .clause( - new RelationViewAtom(personView, p3), - new RelationViewAtom(personView, p4), - new DNFCallAtom(CallPolarity.TRANSITIVE, friendPredicate, p3, p4) + new RelationViewLiteral(personView, p3), + new RelationViewLiteral(personView, p4), + new DnfCallLiteral(CallPolarity.TRANSITIVE, friendPredicate, p3, p4) ) .build(); 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 98995339..99b942d2 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 @@ -2,10 +2,10 @@ package tools.refinery.store.query.viatra; import org.junit.jupiter.api.Test; import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.DNF; +import tools.refinery.store.query.Dnf; import tools.refinery.store.query.ModelQuery; import tools.refinery.store.query.Variable; -import tools.refinery.store.query.atom.RelationViewAtom; +import tools.refinery.store.query.literal.RelationViewLiteral; import tools.refinery.store.query.view.KeyOnlyRelationView; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; @@ -20,9 +20,9 @@ class QueryTransactionTest { var personView = new KeyOnlyRelationView<>(person); var p1 = new Variable("p1"); - var predicate = DNF.builder("TypeConstraint") + var predicate = Dnf.builder("TypeConstraint") .parameters(p1) - .clause(new RelationViewAtom(personView, p1)) + .clause(new RelationViewLiteral(personView, p1)) .build(); var store = ModelStore.builder() -- cgit v1.2.3-70-g09d2