aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java')
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java173
1 files changed, 173 insertions, 0 deletions
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}