diff options
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.java | 173 |
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 @@ | |||
1 | package tools.refinery.store.query.viatra.internal.pquery; | ||
2 | |||
3 | import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; | ||
4 | import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; | ||
5 | import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; | ||
6 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter; | ||
7 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; | ||
8 | import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; | ||
9 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; | ||
10 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery; | ||
11 | import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; | ||
12 | import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; | ||
13 | import tools.refinery.store.query.Constraint; | ||
14 | import tools.refinery.store.query.dnf.Dnf; | ||
15 | import tools.refinery.store.query.dnf.DnfClause; | ||
16 | import tools.refinery.store.query.dnf.DnfUtils; | ||
17 | import tools.refinery.store.query.literal.AbstractCallLiteral; | ||
18 | import tools.refinery.store.query.term.Variable; | ||
19 | import tools.refinery.store.query.view.AnyRelationView; | ||
20 | import tools.refinery.store.query.view.RelationView; | ||
21 | import tools.refinery.store.util.CycleDetectingMapper; | ||
22 | |||
23 | import java.util.*; | ||
24 | import java.util.function.ToIntFunction; | ||
25 | |||
26 | class 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 | } | ||