diff options
Diffstat (limited to 'subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal')
5 files changed, 687 insertions, 20 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java new file mode 100644 index 00000000..40993235 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.internal; | ||
7 | |||
8 | import tools.refinery.store.query.Constraint; | ||
9 | import tools.refinery.store.query.dnf.Dnf; | ||
10 | import tools.refinery.store.query.dnf.DnfBuilder; | ||
11 | import tools.refinery.store.query.dnf.DnfClause; | ||
12 | import tools.refinery.store.query.literal.AbstractCallLiteral; | ||
13 | import tools.refinery.store.query.literal.AbstractCountLiteral; | ||
14 | import tools.refinery.store.query.literal.CallPolarity; | ||
15 | import tools.refinery.store.query.literal.Literal; | ||
16 | import tools.refinery.store.query.term.Aggregator; | ||
17 | import tools.refinery.store.query.term.ConstantTerm; | ||
18 | import tools.refinery.store.query.term.Term; | ||
19 | import tools.refinery.store.query.term.Variable; | ||
20 | import tools.refinery.store.query.term.int_.IntTerms; | ||
21 | import tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms; | ||
22 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
23 | import tools.refinery.store.reasoning.literal.*; | ||
24 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
25 | import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; | ||
26 | import tools.refinery.store.representation.cardinality.UpperCardinalities; | ||
27 | |||
28 | import java.util.*; | ||
29 | import java.util.function.BinaryOperator; | ||
30 | |||
31 | class PartialClauseRewriter { | ||
32 | private final PartialQueryRewriter rewriter; | ||
33 | private final List<Literal> completedLiterals = new ArrayList<>(); | ||
34 | private final Deque<Literal> workList = new ArrayDeque<>(); | ||
35 | private final Set<Variable> positiveVariables = new LinkedHashSet<>(); | ||
36 | private final Set<Variable> unmodifiablePositiveVariables = Collections.unmodifiableSet(positiveVariables); | ||
37 | |||
38 | public PartialClauseRewriter(PartialQueryRewriter rewriter) { | ||
39 | this.rewriter = rewriter; | ||
40 | } | ||
41 | |||
42 | public List<Literal> rewriteClause(DnfClause clause) { | ||
43 | workList.addAll(clause.literals()); | ||
44 | while (!workList.isEmpty()) { | ||
45 | var literal = workList.removeFirst(); | ||
46 | rewrite(literal); | ||
47 | } | ||
48 | return completedLiterals; | ||
49 | } | ||
50 | |||
51 | private void rewrite(Literal literal) { | ||
52 | if (!(literal instanceof AbstractCallLiteral callLiteral)) { | ||
53 | markAsDone(literal); | ||
54 | return; | ||
55 | } | ||
56 | if (callLiteral instanceof CountLowerBoundLiteral countLowerBoundLiteral) { | ||
57 | rewriteCountLowerBound(countLowerBoundLiteral); | ||
58 | return; | ||
59 | } | ||
60 | if (callLiteral instanceof CountUpperBoundLiteral countUpperBoundLiteral) { | ||
61 | rewriteCountUpperBound(countUpperBoundLiteral); | ||
62 | return; | ||
63 | } | ||
64 | if (callLiteral instanceof CountCandidateLowerBoundLiteral countCandidateLowerBoundLiteral) { | ||
65 | rewriteCountCandidateLowerBound(countCandidateLowerBoundLiteral); | ||
66 | return; | ||
67 | } | ||
68 | if (callLiteral instanceof CountCandidateUpperBoundLiteral countCandidateUpperBoundLiteral) { | ||
69 | rewriteCountCandidateUpperBound(countCandidateUpperBoundLiteral); | ||
70 | return; | ||
71 | } | ||
72 | var target = callLiteral.getTarget(); | ||
73 | if (target instanceof Dnf dnf) { | ||
74 | rewriteRecursively(callLiteral, dnf); | ||
75 | } else if (target instanceof ModalConstraint modalConstraint) { | ||
76 | var modality = modalConstraint.modality(); | ||
77 | var concreteness = modalConstraint.concreteness(); | ||
78 | var constraint = modalConstraint.constraint(); | ||
79 | if (constraint instanceof Dnf dnf) { | ||
80 | rewriteRecursively(callLiteral, modality, concreteness, dnf); | ||
81 | } else if (constraint instanceof PartialRelation partialRelation) { | ||
82 | rewrite(callLiteral, modality, concreteness, partialRelation); | ||
83 | } else { | ||
84 | throw new IllegalArgumentException("Cannot interpret modal constraint: " + modalConstraint); | ||
85 | } | ||
86 | } else { | ||
87 | markAsDone(literal); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | private void rewriteCountLowerBound(CountLowerBoundLiteral literal) { | ||
92 | rewritePartialCount(literal, "lower", Modality.MUST, MultiObjectTranslator.LOWER_CARDINALITY_VIEW, 1, | ||
93 | IntTerms::mul, IntTerms.INT_SUM); | ||
94 | } | ||
95 | |||
96 | private void rewriteCountUpperBound(CountUpperBoundLiteral literal) { | ||
97 | rewritePartialCount(literal, "upper", Modality.MAY, MultiObjectTranslator.UPPER_CARDINALITY_VIEW, | ||
98 | UpperCardinalities.ONE, UpperCardinalityTerms::mul, UpperCardinalityTerms.UPPER_CARDINALITY_SUM); | ||
99 | } | ||
100 | |||
101 | private <T> void rewritePartialCount(AbstractCountLiteral<T> literal, String name, Modality modality, | ||
102 | Constraint view, T one, BinaryOperator<Term<T>> mul, Aggregator<T, T> sum) { | ||
103 | var type = literal.getResultType(); | ||
104 | var countResult = computeCountVariables(literal, Concreteness.PARTIAL, name); | ||
105 | var builder = countResult.builder(); | ||
106 | var outputVariable = builder.parameter(type); | ||
107 | var variablesToCount = countResult.variablesToCount(); | ||
108 | |||
109 | var literals = new ArrayList<Literal>(); | ||
110 | literals.add(new ModalConstraint(modality, Concreteness.PARTIAL, literal.getTarget()) | ||
111 | .call(CallPolarity.POSITIVE, countResult.rewrittenArguments())); | ||
112 | switch (variablesToCount.size()) { | ||
113 | case 0 -> literals.add(outputVariable.assign(new ConstantTerm<>(type, one))); | ||
114 | case 1 -> literals.add(view.call(variablesToCount.get(0), | ||
115 | outputVariable)); | ||
116 | default -> { | ||
117 | var firstCount = Variable.of(type); | ||
118 | literals.add(view.call(variablesToCount.get(0), firstCount)); | ||
119 | int length = variablesToCount.size(); | ||
120 | Term<T> accumulator = firstCount; | ||
121 | for (int i = 1; i < length; i++) { | ||
122 | var countVariable = Variable.of(type); | ||
123 | literals.add(view.call(variablesToCount.get(i), countVariable)); | ||
124 | accumulator = mul.apply(accumulator, countVariable); | ||
125 | } | ||
126 | literals.add(outputVariable.assign(accumulator)); | ||
127 | } | ||
128 | } | ||
129 | builder.clause(literals); | ||
130 | |||
131 | var helperQuery = builder.build(); | ||
132 | var aggregationVariable = Variable.of(type); | ||
133 | var helperArguments = countResult.helperArguments(); | ||
134 | helperArguments.add(aggregationVariable); | ||
135 | workList.addFirst(literal.getResultVariable().assign(helperQuery.aggregateBy(aggregationVariable, sum, | ||
136 | helperArguments))); | ||
137 | } | ||
138 | |||
139 | private void rewriteCountCandidateLowerBound(CountCandidateLowerBoundLiteral literal) { | ||
140 | rewriteCandidateCount(literal, "lower", Modality.MAY); | ||
141 | } | ||
142 | |||
143 | private void rewriteCountCandidateUpperBound(CountCandidateUpperBoundLiteral literal) { | ||
144 | rewriteCandidateCount(literal, "upper", Modality.MUST); | ||
145 | } | ||
146 | |||
147 | private void rewriteCandidateCount(AbstractCountLiteral<Integer> literal, String name, Modality modality) { | ||
148 | var countResult = computeCountVariables(literal, Concreteness.CANDIDATE, name); | ||
149 | var builder = countResult.builder(); | ||
150 | |||
151 | var literals = new ArrayList<Literal>(); | ||
152 | literals.add(new ModalConstraint(modality, Concreteness.CANDIDATE, literal.getTarget()) | ||
153 | .call(CallPolarity.POSITIVE, countResult.rewrittenArguments())); | ||
154 | for (var variable : countResult.variablesToCount()) { | ||
155 | literals.add(new ModalConstraint(modality, Concreteness.CANDIDATE, ReasoningAdapter.EXISTS_SYMBOL) | ||
156 | .call(variable)); | ||
157 | } | ||
158 | builder.clause(literals); | ||
159 | |||
160 | var helperQuery = builder.build(); | ||
161 | workList.addFirst(literal.getResultVariable().assign(helperQuery.count(countResult.helperArguments()))); | ||
162 | } | ||
163 | |||
164 | private CountResult computeCountVariables(AbstractCountLiteral<?> literal, Concreteness concreteness, | ||
165 | String name) { | ||
166 | var target = literal.getTarget(); | ||
167 | int arity = target.arity(); | ||
168 | var parameters = target.getParameters(); | ||
169 | var literalArguments = literal.getArguments(); | ||
170 | var privateVariables = literal.getPrivateVariables(positiveVariables); | ||
171 | var builder = Dnf.builder("%s#%s#%s".formatted(target.name(), concreteness, name)); | ||
172 | var rewrittenArguments = new ArrayList<Variable>(parameters.size()); | ||
173 | var variablesToCount = new ArrayList<Variable>(); | ||
174 | var helperArguments = new ArrayList<Variable>(); | ||
175 | var literalToRewrittenArgumentMap = new HashMap<Variable, Variable>(); | ||
176 | for (int i = 0; i < arity; i++) { | ||
177 | var literalArgument = literalArguments.get(i); | ||
178 | var parameter = parameters.get(i); | ||
179 | var rewrittenArgument = literalToRewrittenArgumentMap.computeIfAbsent(literalArgument, key -> { | ||
180 | helperArguments.add(key); | ||
181 | var newArgument = builder.parameter(parameter); | ||
182 | if (privateVariables.contains(key)) { | ||
183 | variablesToCount.add(newArgument); | ||
184 | } | ||
185 | return newArgument; | ||
186 | }); | ||
187 | rewrittenArguments.add(rewrittenArgument); | ||
188 | } | ||
189 | return new CountResult(builder, rewrittenArguments, helperArguments, variablesToCount); | ||
190 | } | ||
191 | |||
192 | private void markAsDone(Literal literal) { | ||
193 | completedLiterals.add(literal); | ||
194 | positiveVariables.addAll(literal.getOutputVariables()); | ||
195 | } | ||
196 | |||
197 | private void rewriteRecursively(AbstractCallLiteral callLiteral, Modality modality, Concreteness concreteness, | ||
198 | Dnf dnf) { | ||
199 | var liftedDnf = rewriter.getLifter().lift(modality, concreteness, dnf); | ||
200 | rewriteRecursively(callLiteral, liftedDnf); | ||
201 | } | ||
202 | |||
203 | private void rewriteRecursively(AbstractCallLiteral callLiteral, Dnf dnf) { | ||
204 | var rewrittenDnf = rewriter.rewrite(dnf); | ||
205 | var rewrittenLiteral = callLiteral.withTarget(rewrittenDnf); | ||
206 | markAsDone(rewrittenLiteral); | ||
207 | } | ||
208 | |||
209 | private void rewrite(AbstractCallLiteral callLiteral, Modality modality, Concreteness concreteness, | ||
210 | PartialRelation partialRelation) { | ||
211 | var relationRewriter = rewriter.getRelationRewriter(partialRelation); | ||
212 | var literals = relationRewriter.rewriteLiteral( | ||
213 | unmodifiablePositiveVariables, callLiteral, modality, concreteness); | ||
214 | int length = literals.size(); | ||
215 | for (int i = length - 1; i >= 0; i--) { | ||
216 | workList.addFirst(literals.get(i)); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | private record CountResult(DnfBuilder builder, List<Variable> rewrittenArguments, List<Variable> helperArguments, | ||
221 | List<Variable> variablesToCount) { | ||
222 | } | ||
223 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialQueryRewriter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialQueryRewriter.java new file mode 100644 index 00000000..79cba263 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialQueryRewriter.java | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.internal; | ||
7 | |||
8 | import tools.refinery.store.query.dnf.Dnf; | ||
9 | import tools.refinery.store.query.rewriter.AbstractRecursiveRewriter; | ||
10 | import tools.refinery.store.reasoning.interpretation.PartialRelationRewriter; | ||
11 | import tools.refinery.store.reasoning.lifting.DnfLifter; | ||
12 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
13 | |||
14 | import java.util.HashMap; | ||
15 | import java.util.Map; | ||
16 | |||
17 | class PartialQueryRewriter extends AbstractRecursiveRewriter { | ||
18 | private final DnfLifter lifter; | ||
19 | private final Map<PartialRelation, PartialRelationRewriter> relationRewriterMap = new HashMap<>(); | ||
20 | |||
21 | PartialQueryRewriter(DnfLifter lifter) { | ||
22 | this.lifter = lifter; | ||
23 | } | ||
24 | |||
25 | DnfLifter getLifter() { | ||
26 | return lifter; | ||
27 | } | ||
28 | |||
29 | PartialRelationRewriter getRelationRewriter(PartialRelation partialRelation) { | ||
30 | var rewriter = relationRewriterMap.get(partialRelation); | ||
31 | if (rewriter == null) { | ||
32 | throw new IllegalArgumentException("Do not know how to interpret partial relation: " + partialRelation); | ||
33 | } | ||
34 | return rewriter; | ||
35 | } | ||
36 | |||
37 | public void addRelationRewriter(PartialRelation partialRelation, PartialRelationRewriter interpreter) { | ||
38 | if (relationRewriterMap.put(partialRelation, interpreter) != null) { | ||
39 | throw new IllegalArgumentException("Duplicate partial relation: " + partialRelation); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | @Override | ||
44 | protected Dnf doRewrite(Dnf dnf) { | ||
45 | var builder = Dnf.builderFrom(dnf); | ||
46 | for (var clause : dnf.getClauses()) { | ||
47 | var clauseRewriter = new PartialClauseRewriter(this); | ||
48 | var rewrittenClauses = clauseRewriter.rewriteClause(clause); | ||
49 | builder.clause(rewrittenClauses); | ||
50 | } | ||
51 | return builder.build(); | ||
52 | } | ||
53 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java index 1bd3ad2e..bd16bdfa 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java | |||
@@ -5,20 +5,114 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.reasoning.internal; | 6 | package tools.refinery.store.reasoning.internal; |
7 | 7 | ||
8 | import org.jetbrains.annotations.Nullable; | ||
9 | import tools.refinery.store.model.Interpretation; | ||
8 | import tools.refinery.store.model.Model; | 10 | import tools.refinery.store.model.Model; |
9 | import tools.refinery.store.reasoning.ReasoningAdapter; | 11 | import tools.refinery.store.reasoning.ReasoningAdapter; |
10 | import tools.refinery.store.reasoning.PartialInterpretation; | 12 | import tools.refinery.store.reasoning.interpretation.AnyPartialInterpretation; |
13 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | ||
14 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
15 | import tools.refinery.store.reasoning.refinement.AnyPartialInterpretationRefiner; | ||
16 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | ||
17 | import tools.refinery.store.reasoning.refinement.StorageRefiner; | ||
18 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; | ||
11 | import tools.refinery.store.reasoning.representation.PartialSymbol; | 19 | import tools.refinery.store.reasoning.representation.PartialSymbol; |
12 | import tools.refinery.store.query.dnf.Dnf; | 20 | import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; |
13 | import tools.refinery.store.query.resultset.ResultSet; | 21 | import tools.refinery.store.representation.Symbol; |
22 | import tools.refinery.store.representation.cardinality.CardinalityInterval; | ||
23 | import tools.refinery.store.representation.cardinality.CardinalityIntervals; | ||
24 | import tools.refinery.store.tuple.Tuple; | ||
25 | import tools.refinery.store.tuple.Tuple1; | ||
14 | 26 | ||
15 | public class ReasoningAdapterImpl implements ReasoningAdapter { | 27 | import java.util.HashMap; |
28 | import java.util.Map; | ||
29 | |||
30 | class ReasoningAdapterImpl implements ReasoningAdapter { | ||
31 | static final Symbol<Integer> NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0); | ||
16 | private final Model model; | 32 | private final Model model; |
17 | private final ReasoningStoreAdapterImpl storeAdapter; | 33 | private final ReasoningStoreAdapterImpl storeAdapter; |
34 | private final Map<AnyPartialSymbol, AnyPartialInterpretation>[] partialInterpretations; | ||
35 | private final Map<AnyPartialSymbol, AnyPartialInterpretationRefiner> refiners; | ||
36 | private final StorageRefiner[] storageRefiners; | ||
37 | private final Interpretation<Integer> nodeCountInterpretation; | ||
38 | private final Interpretation<CardinalityInterval> countInterpretation; | ||
18 | 39 | ||
19 | ReasoningAdapterImpl(Model model, ReasoningStoreAdapterImpl storeAdapter) { | 40 | ReasoningAdapterImpl(Model model, ReasoningStoreAdapterImpl storeAdapter) { |
20 | this.model = model; | 41 | this.model = model; |
21 | this.storeAdapter = storeAdapter; | 42 | this.storeAdapter = storeAdapter; |
43 | |||
44 | int concretenessLength = Concreteness.values().length; | ||
45 | // Creation of a generic array. | ||
46 | @SuppressWarnings({"unchecked", "squid:S1905"}) | ||
47 | var interpretationsArray = (Map<AnyPartialSymbol, AnyPartialInterpretation>[]) new Map[concretenessLength]; | ||
48 | partialInterpretations = interpretationsArray; | ||
49 | createPartialInterpretations(); | ||
50 | |||
51 | var refinerFactories = storeAdapter.getSymbolRefiners(); | ||
52 | refiners = new HashMap<>(refinerFactories.size()); | ||
53 | createRefiners(); | ||
54 | |||
55 | storageRefiners = storeAdapter.createStorageRefiner(model); | ||
56 | |||
57 | nodeCountInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL); | ||
58 | if (model.getStore().getSymbols().contains(MultiObjectTranslator.COUNT_STORAGE)) { | ||
59 | countInterpretation = model.getInterpretation(MultiObjectTranslator.COUNT_STORAGE); | ||
60 | } else { | ||
61 | countInterpretation = null; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | private void createPartialInterpretations() { | ||
66 | var supportedInterpretations = storeAdapter.getSupportedInterpretations(); | ||
67 | int concretenessLength = Concreteness.values().length; | ||
68 | var interpretationFactories = storeAdapter.getSymbolInterpreters(); | ||
69 | for (int i = 0; i < concretenessLength; i++) { | ||
70 | var concreteness = Concreteness.values()[i]; | ||
71 | if (supportedInterpretations.contains(concreteness)) { | ||
72 | partialInterpretations[i] = new HashMap<>(interpretationFactories.size()); | ||
73 | } | ||
74 | } | ||
75 | // Create the partial interpretations in order so that factories may refer to interpretations of symbols | ||
76 | // preceding them in the ordered {@code interpretationFactories} map, e.g., for opposite interpretations. | ||
77 | for (var entry : interpretationFactories.entrySet()) { | ||
78 | var partialSymbol = entry.getKey(); | ||
79 | var factory = entry.getValue(); | ||
80 | for (int i = 0; i < concretenessLength; i++) { | ||
81 | if (partialInterpretations[i] != null) { | ||
82 | var concreteness = Concreteness.values()[i]; | ||
83 | var interpretation = createPartialInterpretation(concreteness, factory, partialSymbol); | ||
84 | partialInterpretations[i].put(partialSymbol, interpretation); | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | private <A, C> PartialInterpretation<A, C> createPartialInterpretation( | ||
91 | Concreteness concreteness, PartialInterpretation.Factory<A, C> interpreter, AnyPartialSymbol symbol) { | ||
92 | // The builder only allows well-typed assignment of interpreters to symbols. | ||
93 | @SuppressWarnings("unchecked") | ||
94 | var typedSymbol = (PartialSymbol<A, C>) symbol; | ||
95 | return interpreter.create(this, concreteness, typedSymbol); | ||
96 | } | ||
97 | |||
98 | private void createRefiners() { | ||
99 | var refinerFactories = storeAdapter.getSymbolRefiners(); | ||
100 | // Create the partial interpretations refiners in order so that factories may refer to refiners of symbols | ||
101 | // preceding them in the ordered {@code interpretationFactories} map, e.g., for opposite interpretations. | ||
102 | for (var entry : refinerFactories.entrySet()) { | ||
103 | var partialSymbol = entry.getKey(); | ||
104 | var factory = entry.getValue(); | ||
105 | var refiner = createRefiner(factory, partialSymbol); | ||
106 | refiners.put(partialSymbol, refiner); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | private <A, C> PartialInterpretationRefiner<A, C> createRefiner( | ||
111 | PartialInterpretationRefiner.Factory<A, C> factory, AnyPartialSymbol symbol) { | ||
112 | // The builder only allows well-typed assignment of interpreters to symbols. | ||
113 | @SuppressWarnings("unchecked") | ||
114 | var typedSymbol = (PartialSymbol<A, C>) symbol; | ||
115 | return factory.create(this, typedSymbol); | ||
22 | } | 116 | } |
23 | 117 | ||
24 | @Override | 118 | @Override |
@@ -32,12 +126,88 @@ public class ReasoningAdapterImpl implements ReasoningAdapter { | |||
32 | } | 126 | } |
33 | 127 | ||
34 | @Override | 128 | @Override |
35 | public <A, C> PartialInterpretation<A, C> getPartialInterpretation(PartialSymbol<A, C> partialSymbol) { | 129 | public <A, C> PartialInterpretation<A, C> getPartialInterpretation(Concreteness concreteness, |
36 | return null; | 130 | PartialSymbol<A, C> partialSymbol) { |
131 | var map = partialInterpretations[concreteness.ordinal()]; | ||
132 | if (map == null) { | ||
133 | throw new IllegalArgumentException("No interpretation for concreteness: " + concreteness); | ||
134 | } | ||
135 | var interpretation = map.get(partialSymbol); | ||
136 | if (interpretation == null) { | ||
137 | throw new IllegalArgumentException("No interpretation for partial symbol: " + partialSymbol); | ||
138 | } | ||
139 | // The builder only allows well-typed assignment of interpreters to symbols. | ||
140 | @SuppressWarnings("unchecked") | ||
141 | var typedInterpretation = (PartialInterpretation<A, C>) interpretation; | ||
142 | return typedInterpretation; | ||
143 | } | ||
144 | |||
145 | @Override | ||
146 | public <A, C> PartialInterpretationRefiner<A, C> getRefiner(PartialSymbol<A, C> partialSymbol) { | ||
147 | var refiner = refiners.get(partialSymbol); | ||
148 | if (refiner == null) { | ||
149 | throw new IllegalArgumentException("No refiner for partial symbol: " + partialSymbol); | ||
150 | } | ||
151 | // The builder only allows well-typed assignment of refiners to symbols. | ||
152 | @SuppressWarnings("unchecked") | ||
153 | var typedRefiner = (PartialInterpretationRefiner<A, C>) refiner; | ||
154 | return typedRefiner; | ||
155 | } | ||
156 | |||
157 | @Override | ||
158 | @Nullable | ||
159 | public Tuple1 split(int parentNode) { | ||
160 | int newNodeId = nodeCountInterpretation.get(Tuple.of()); | ||
161 | nodeCountInterpretation.put(Tuple.of(), newNodeId + 1); | ||
162 | // Avoid creating an iterator object. | ||
163 | //noinspection ForLoopReplaceableByForEach | ||
164 | for (int i = 0; i < storageRefiners.length; i++) { | ||
165 | if (!storageRefiners[i].split(parentNode, newNodeId)) { | ||
166 | return null; | ||
167 | } | ||
168 | } | ||
169 | return Tuple.of(newNodeId); | ||
170 | } | ||
171 | |||
172 | @Override | ||
173 | public @Nullable Tuple1 focus(int parentObject) { | ||
174 | if (countInterpretation == null) { | ||
175 | throw new IllegalStateException("Cannot focus without " + MultiObjectTranslator.class.getSimpleName()); | ||
176 | } | ||
177 | var tuple = Tuple.of(parentObject); | ||
178 | var count = countInterpretation.get(tuple); | ||
179 | if (CardinalityIntervals.ONE.equals(count)) { | ||
180 | return tuple; | ||
181 | } | ||
182 | if (CardinalityIntervals.LONE.equals(count)) { | ||
183 | countInterpretation.put(tuple, CardinalityIntervals.ONE); | ||
184 | return tuple; | ||
185 | } | ||
186 | if (CardinalityIntervals.NONE.equals(count)) { | ||
187 | return null; | ||
188 | } | ||
189 | return split(parentObject); | ||
190 | } | ||
191 | |||
192 | @Override | ||
193 | public boolean cleanup(int nodeToDelete) { | ||
194 | // Avoid creating an iterator object. | ||
195 | //noinspection ForLoopReplaceableByForEach | ||
196 | for (int i = 0; i < storageRefiners.length; i++) { | ||
197 | if (!storageRefiners[i].cleanup(nodeToDelete)) { | ||
198 | return false; | ||
199 | } | ||
200 | } | ||
201 | int currentModelSize = nodeCountInterpretation.get(Tuple.of()); | ||
202 | if (nodeToDelete == currentModelSize - 1) { | ||
203 | nodeCountInterpretation.put(Tuple.of(), nodeToDelete); | ||
204 | } | ||
205 | return true; | ||
37 | } | 206 | } |
38 | 207 | ||
39 | @Override | 208 | @Override |
40 | public ResultSet<Boolean> getLiftedResultSet(Dnf query) { | 209 | public int getNodeCount() { |
41 | return null; | 210 | Integer nodeCount = nodeCountInterpretation.get(Tuple.of()); |
211 | return nodeCount == null ? 0 : nodeCount; | ||
42 | } | 212 | } |
43 | } | 213 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java index aa71496c..722458c8 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java | |||
@@ -6,26 +6,169 @@ | |||
6 | package tools.refinery.store.reasoning.internal; | 6 | package tools.refinery.store.reasoning.internal; |
7 | 7 | ||
8 | import tools.refinery.store.adapter.AbstractModelAdapterBuilder; | 8 | import tools.refinery.store.adapter.AbstractModelAdapterBuilder; |
9 | import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; | ||
10 | import tools.refinery.store.dse.transition.objectives.Objective; | ||
11 | import tools.refinery.store.dse.transition.objectives.Objectives; | ||
9 | import tools.refinery.store.model.ModelStore; | 12 | import tools.refinery.store.model.ModelStore; |
13 | import tools.refinery.store.model.ModelStoreBuilder; | ||
14 | import tools.refinery.store.query.ModelQueryBuilder; | ||
10 | import tools.refinery.store.query.dnf.Dnf; | 15 | import tools.refinery.store.query.dnf.Dnf; |
16 | import tools.refinery.store.query.dnf.FunctionalQuery; | ||
17 | import tools.refinery.store.query.dnf.Query; | ||
18 | import tools.refinery.store.query.dnf.RelationalQuery; | ||
11 | import tools.refinery.store.reasoning.ReasoningBuilder; | 19 | import tools.refinery.store.reasoning.ReasoningBuilder; |
20 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | ||
21 | import tools.refinery.store.reasoning.lifting.DnfLifter; | ||
22 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
12 | import tools.refinery.store.reasoning.literal.Modality; | 23 | import tools.refinery.store.reasoning.literal.Modality; |
24 | import tools.refinery.store.reasoning.refinement.DefaultStorageRefiner; | ||
25 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | ||
26 | import tools.refinery.store.reasoning.refinement.PartialModelInitializer; | ||
27 | import tools.refinery.store.reasoning.refinement.StorageRefiner; | ||
28 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; | ||
29 | import tools.refinery.store.reasoning.translator.AnyPartialSymbolTranslator; | ||
30 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | ||
31 | import tools.refinery.store.representation.AnySymbol; | ||
32 | import tools.refinery.store.representation.Symbol; | ||
33 | import tools.refinery.store.statecoding.StateCoderBuilder; | ||
34 | |||
35 | import java.util.*; | ||
13 | 36 | ||
14 | public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder<ReasoningStoreAdapterImpl> | 37 | public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder<ReasoningStoreAdapterImpl> |
15 | implements ReasoningBuilder { | 38 | implements ReasoningBuilder { |
39 | private final DnfLifter lifter = new DnfLifter(); | ||
40 | private final PartialQueryRewriter queryRewriter = new PartialQueryRewriter(lifter); | ||
41 | private Set<Concreteness> requiredInterpretations = Set.of(Concreteness.values()); | ||
42 | private final Map<AnyPartialSymbol, AnyPartialSymbolTranslator> translators = new LinkedHashMap<>(); | ||
43 | private final Map<AnyPartialSymbol, PartialInterpretation.Factory<?, ?>> symbolInterpreters = | ||
44 | new LinkedHashMap<>(); | ||
45 | private final Map<AnyPartialSymbol, PartialInterpretationRefiner.Factory<?, ?>> symbolRefiners = | ||
46 | new LinkedHashMap<>(); | ||
47 | private final Map<AnySymbol, StorageRefiner.Factory<?>> registeredStorageRefiners = new LinkedHashMap<>(); | ||
48 | private final List<PartialModelInitializer> initializers = new ArrayList<>(); | ||
49 | private final List<Objective> objectives = new ArrayList<>(); | ||
50 | |||
51 | @Override | ||
52 | public ReasoningBuilder requiredInterpretations(Collection<Concreteness> requiredInterpretations) { | ||
53 | this.requiredInterpretations = Set.copyOf(requiredInterpretations); | ||
54 | return this; | ||
55 | } | ||
56 | |||
57 | @Override | ||
58 | public ReasoningBuilder partialSymbol(AnyPartialSymbolTranslator translator) { | ||
59 | var partialSymbol = translator.getPartialSymbol(); | ||
60 | var oldConfiguration = translators.put(partialSymbol, translator); | ||
61 | if (oldConfiguration != null && oldConfiguration != translator) { | ||
62 | throw new IllegalArgumentException("Duplicate configuration for symbol: " + partialSymbol); | ||
63 | } | ||
64 | return this; | ||
65 | } | ||
66 | |||
16 | @Override | 67 | @Override |
17 | public ReasoningBuilder liftedQuery(Dnf liftedQuery) { | 68 | public <T> ReasoningBuilder storageRefiner(Symbol<T> symbol, StorageRefiner.Factory<T> refiner) { |
18 | return null; | 69 | checkNotConfigured(); |
70 | if (registeredStorageRefiners.put(symbol, refiner) != null) { | ||
71 | throw new IllegalArgumentException("Duplicate representation refiner for symbol: " + symbol); | ||
72 | } | ||
73 | return this; | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public ReasoningBuilder initializer(PartialModelInitializer initializer) { | ||
78 | checkNotConfigured(); | ||
79 | initializers.add(initializer); | ||
80 | return this; | ||
19 | } | 81 | } |
20 | 82 | ||
21 | @Override | 83 | @Override |
22 | public Dnf lift(Modality modality, Dnf query) { | 84 | public ReasoningBuilder objective(Objective objective) { |
23 | checkNotConfigured(); | 85 | checkNotConfigured(); |
24 | return null; | 86 | objectives.add(objective); |
87 | return this; | ||
88 | } | ||
89 | |||
90 | @Override | ||
91 | public <T> Query<T> lift(Modality modality, Concreteness concreteness, Query<T> query) { | ||
92 | return lifter.lift(modality, concreteness, query); | ||
93 | } | ||
94 | |||
95 | @Override | ||
96 | public RelationalQuery lift(Modality modality, Concreteness concreteness, RelationalQuery query) { | ||
97 | return lifter.lift(modality, concreteness, query); | ||
98 | } | ||
99 | |||
100 | @Override | ||
101 | public <T> FunctionalQuery<T> lift(Modality modality, Concreteness concreteness, FunctionalQuery<T> query) { | ||
102 | return lifter.lift(modality, concreteness, query); | ||
103 | } | ||
104 | |||
105 | @Override | ||
106 | public Dnf lift(Modality modality, Concreteness concreteness, Dnf dnf) { | ||
107 | return lifter.lift(modality, concreteness, dnf); | ||
108 | } | ||
109 | |||
110 | @Override | ||
111 | protected void doConfigure(ModelStoreBuilder storeBuilder) { | ||
112 | storeBuilder.symbols(ReasoningAdapterImpl.NODE_COUNT_SYMBOL); | ||
113 | storeBuilder.tryGetAdapter(StateCoderBuilder.class) | ||
114 | .ifPresent(stateCoderBuilder -> stateCoderBuilder.exclude(ReasoningAdapterImpl.NODE_COUNT_SYMBOL)); | ||
115 | for (var translator : translators.values()) { | ||
116 | translator.configure(storeBuilder); | ||
117 | if (translator instanceof PartialRelationTranslator relationConfiguration) { | ||
118 | doConfigure(storeBuilder, relationConfiguration); | ||
119 | } else { | ||
120 | throw new IllegalArgumentException("Unknown partial symbol translator %s for partial symbol %s" | ||
121 | .formatted(translator, translator.getPartialSymbol())); | ||
122 | } | ||
123 | } | ||
124 | storeBuilder.symbols(registeredStorageRefiners.keySet()); | ||
125 | var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); | ||
126 | queryBuilder.rewriter(queryRewriter); | ||
127 | if (!objectives.isEmpty()) { | ||
128 | storeBuilder.tryGetAdapter(DesignSpaceExplorationBuilder.class) | ||
129 | .ifPresent(dseBuilder -> dseBuilder.objective(Objectives.sum(objectives))); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | private void doConfigure(ModelStoreBuilder storeBuilder, PartialRelationTranslator relationConfiguration) { | ||
134 | var partialRelation = relationConfiguration.getPartialRelation(); | ||
135 | queryRewriter.addRelationRewriter(partialRelation, relationConfiguration.getRewriter()); | ||
136 | var interpretationFactory = relationConfiguration.getInterpretationFactory(); | ||
137 | interpretationFactory.configure(storeBuilder, requiredInterpretations); | ||
138 | symbolInterpreters.put(partialRelation, interpretationFactory); | ||
139 | var refiner = relationConfiguration.getInterpretationRefiner(); | ||
140 | if (refiner != null) { | ||
141 | symbolRefiners.put(partialRelation, refiner); | ||
142 | } | ||
25 | } | 143 | } |
26 | 144 | ||
27 | @Override | 145 | @Override |
28 | public ReasoningStoreAdapterImpl doBuild(ModelStore store) { | 146 | public ReasoningStoreAdapterImpl doBuild(ModelStore store) { |
29 | return null; | 147 | return new ReasoningStoreAdapterImpl(store, requiredInterpretations, |
148 | Collections.unmodifiableMap(symbolInterpreters), Collections.unmodifiableMap(symbolRefiners), | ||
149 | getStorageRefiners(store), Collections.unmodifiableList(initializers)); | ||
150 | } | ||
151 | |||
152 | private Map<AnySymbol, StorageRefiner.Factory<?>> getStorageRefiners(ModelStore store) { | ||
153 | var symbols = store.getSymbols(); | ||
154 | var storageRefiners = new LinkedHashMap<AnySymbol, StorageRefiner.Factory<?>>(symbols.size()); | ||
155 | for (var symbol : symbols) { | ||
156 | var refiner = registeredStorageRefiners.remove(symbol); | ||
157 | if (refiner == null) { | ||
158 | if (symbol.arity() == 0) { | ||
159 | // Arity-0 symbols don't need a default refiner, because they are unaffected by object | ||
160 | // creation/removal. Only a custom refiner ({@code refiner != null}) might need to update them. | ||
161 | continue; | ||
162 | } | ||
163 | // By default, copy over all affected tuples on object creation and remove all affected tuples on | ||
164 | // object removal. | ||
165 | refiner = DefaultStorageRefiner.factory(); | ||
166 | } | ||
167 | storageRefiners.put(symbol, refiner); | ||
168 | } | ||
169 | if (!registeredStorageRefiners.isEmpty()) { | ||
170 | throw new IllegalArgumentException("Unused storage refiners: " + registeredStorageRefiners.keySet()); | ||
171 | } | ||
172 | return Collections.unmodifiableMap(storageRefiners); | ||
30 | } | 173 | } |
31 | } | 174 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java index cdddd8d6..9ef6fb16 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java | |||
@@ -5,19 +5,46 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.reasoning.internal; | 6 | package tools.refinery.store.reasoning.internal; |
7 | 7 | ||
8 | import tools.refinery.store.reasoning.ReasoningStoreAdapter; | 8 | import tools.refinery.store.dse.propagation.PropagationAdapter; |
9 | import tools.refinery.store.model.Model; | 9 | import tools.refinery.store.model.Model; |
10 | import tools.refinery.store.model.ModelStore; | 10 | import tools.refinery.store.model.ModelStore; |
11 | import tools.refinery.store.query.ModelQueryAdapter; | ||
12 | import tools.refinery.store.reasoning.ReasoningStoreAdapter; | ||
13 | import tools.refinery.store.reasoning.interpretation.PartialInterpretation; | ||
14 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
15 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | ||
16 | import tools.refinery.store.reasoning.refinement.PartialModelInitializer; | ||
17 | import tools.refinery.store.reasoning.refinement.StorageRefiner; | ||
11 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; | 18 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; |
12 | import tools.refinery.store.query.dnf.Dnf; | 19 | import tools.refinery.store.reasoning.seed.ModelSeed; |
20 | import tools.refinery.store.representation.AnySymbol; | ||
21 | import tools.refinery.store.representation.Symbol; | ||
22 | import tools.refinery.store.tuple.Tuple; | ||
13 | 23 | ||
14 | import java.util.Collection; | 24 | import java.util.Collection; |
25 | import java.util.List; | ||
26 | import java.util.Map; | ||
27 | import java.util.Set; | ||
15 | 28 | ||
16 | public class ReasoningStoreAdapterImpl implements ReasoningStoreAdapter { | 29 | class ReasoningStoreAdapterImpl implements ReasoningStoreAdapter { |
17 | private final ModelStore store; | 30 | private final ModelStore store; |
31 | private final Set<Concreteness> supportedInterpretations; | ||
32 | private final Map<AnyPartialSymbol, PartialInterpretation.Factory<?, ?>> symbolInterpreters; | ||
33 | private final Map<AnyPartialSymbol, PartialInterpretationRefiner.Factory<?, ?>> symbolRefiners; | ||
34 | private final Map<AnySymbol, StorageRefiner.Factory<?>> storageRefiners; | ||
35 | private final List<PartialModelInitializer> initializers; | ||
18 | 36 | ||
19 | ReasoningStoreAdapterImpl(ModelStore store) { | 37 | ReasoningStoreAdapterImpl(ModelStore store, Set<Concreteness> supportedInterpretations, |
38 | Map<AnyPartialSymbol, PartialInterpretation.Factory<?, ?>> symbolInterpreters, | ||
39 | Map<AnyPartialSymbol, PartialInterpretationRefiner.Factory<?, ?>> symbolRefiners, | ||
40 | Map<AnySymbol, StorageRefiner.Factory<?>> storageRefiners, | ||
41 | List<PartialModelInitializer> initializers) { | ||
20 | this.store = store; | 42 | this.store = store; |
43 | this.supportedInterpretations = supportedInterpretations; | ||
44 | this.symbolInterpreters = symbolInterpreters; | ||
45 | this.symbolRefiners = symbolRefiners; | ||
46 | this.storageRefiners = storageRefiners; | ||
47 | this.initializers = initializers; | ||
21 | } | 48 | } |
22 | 49 | ||
23 | @Override | 50 | @Override |
@@ -26,13 +53,64 @@ public class ReasoningStoreAdapterImpl implements ReasoningStoreAdapter { | |||
26 | } | 53 | } |
27 | 54 | ||
28 | @Override | 55 | @Override |
56 | public Set<Concreteness> getSupportedInterpretations() { | ||
57 | return supportedInterpretations; | ||
58 | } | ||
59 | |||
60 | @Override | ||
29 | public Collection<AnyPartialSymbol> getPartialSymbols() { | 61 | public Collection<AnyPartialSymbol> getPartialSymbols() { |
30 | return null; | 62 | return symbolInterpreters.keySet(); |
31 | } | 63 | } |
32 | 64 | ||
33 | @Override | 65 | @Override |
34 | public Collection<Dnf> getLiftedQueries() { | 66 | public Collection<AnyPartialSymbol> getRefinablePartialSymbols() { |
35 | return null; | 67 | return symbolRefiners.keySet(); |
68 | } | ||
69 | |||
70 | // Use of wildcard return value only in internal method not exposed as API, so there is less chance of confusion. | ||
71 | @SuppressWarnings("squid:S1452") | ||
72 | Map<AnyPartialSymbol, PartialInterpretation.Factory<?, ?>> getSymbolInterpreters() { | ||
73 | return symbolInterpreters; | ||
74 | } | ||
75 | |||
76 | // Use of wildcard return value only in internal method not exposed as API, so there is less chance of confusion. | ||
77 | @SuppressWarnings("squid:S1452") | ||
78 | Map<AnyPartialSymbol, PartialInterpretationRefiner.Factory<?, ?>> getSymbolRefiners() { | ||
79 | return symbolRefiners; | ||
80 | } | ||
81 | |||
82 | StorageRefiner[] createStorageRefiner(Model model) { | ||
83 | var refiners = new StorageRefiner[storageRefiners.size()]; | ||
84 | int i = 0; | ||
85 | for (var entry : storageRefiners.entrySet()) { | ||
86 | var symbol = entry.getKey(); | ||
87 | var factory = entry.getValue(); | ||
88 | refiners[i] = createStorageRefiner(factory, model, symbol); | ||
89 | i++; | ||
90 | } | ||
91 | return refiners; | ||
92 | } | ||
93 | |||
94 | private <T> StorageRefiner createStorageRefiner(StorageRefiner.Factory<T> factory, Model model, AnySymbol symbol) { | ||
95 | // The builder only allows well-typed assignment of refiners to symbols. | ||
96 | @SuppressWarnings("unchecked") | ||
97 | var typedSymbol = (Symbol<T>) symbol; | ||
98 | return factory.create(typedSymbol, model); | ||
99 | } | ||
100 | |||
101 | public Model createInitialModel(ModelSeed modelSeed) { | ||
102 | var model = store.createEmptyModel(); | ||
103 | model.getInterpretation(ReasoningAdapterImpl.NODE_COUNT_SYMBOL).put(Tuple.of(), modelSeed.getNodeCount()); | ||
104 | for (var initializer : initializers) { | ||
105 | initializer.initialize(model, modelSeed); | ||
106 | } | ||
107 | model.tryGetAdapter(PropagationAdapter.class).ifPresent(propagationAdapter -> { | ||
108 | if (propagationAdapter.propagate().isRejected()) { | ||
109 | throw new IllegalArgumentException("Inconsistent initial mode: propagation failed"); | ||
110 | } | ||
111 | }); | ||
112 | model.getAdapter(ModelQueryAdapter.class).flushChanges(); | ||
113 | return model; | ||
36 | } | 114 | } |
37 | 115 | ||
38 | @Override | 116 | @Override |