diff options
author | Kristóf Marussy <marussy@mit.bme.hu> | 2024-08-01 18:27:10 +0200 |
---|---|---|
committer | Kristóf Marussy <marussy@mit.bme.hu> | 2024-08-01 18:27:10 +0200 |
commit | 228a54b5b4cf21a4dbf5e45fd50361cec3068f35 (patch) | |
tree | c7fe76c9b293886c20814b8e814e4b4c09270a54 /subprojects/store-reasoning | |
parent | refactor(reasoning): propagation in refiner (diff) | |
download | refinery-228a54b5b4cf21a4dbf5e45fd50361cec3068f35.tar.gz refinery-228a54b5b4cf21a4dbf5e45fd50361cec3068f35.tar.zst refinery-228a54b5b4cf21a4dbf5e45fd50361cec3068f35.zip |
feat: base predicates
Diffstat (limited to 'subprojects/store-reasoning')
5 files changed, 252 insertions, 7 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java index 7a959991..4471b193 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java | |||
@@ -21,9 +21,9 @@ class DirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, Bo | |||
21 | private PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; | 21 | private PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; |
22 | private PartialInterpretationRefiner<TruthValue, Boolean> targetRefiner; | 22 | private PartialInterpretationRefiner<TruthValue, Boolean> targetRefiner; |
23 | 23 | ||
24 | public DirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | 24 | protected DirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, |
25 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType, | 25 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType, |
26 | PartialRelation targetType) { | 26 | PartialRelation targetType) { |
27 | super(adapter, partialSymbol, concreteSymbol); | 27 | super(adapter, partialSymbol, concreteSymbol); |
28 | this.sourceType = sourceType; | 28 | this.sourceType = sourceType; |
29 | this.targetType = targetType; | 29 | this.targetType = targetType; |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java index 79251d13..93ed30f6 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java | |||
@@ -21,8 +21,9 @@ class UndirectedCrossReferenceRefiner extends ConcreteSymbolRefiner<TruthValue, | |||
21 | private final PartialRelation sourceType; | 21 | private final PartialRelation sourceType; |
22 | private PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; | 22 | private PartialInterpretationRefiner<TruthValue, Boolean> sourceRefiner; |
23 | 23 | ||
24 | public UndirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | 24 | protected UndirectedCrossReferenceRefiner(ReasoningAdapter adapter, |
25 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType) { | 25 | PartialSymbol<TruthValue, Boolean> partialSymbol, |
26 | Symbol<TruthValue> concreteSymbol, PartialRelation sourceType) { | ||
26 | super(adapter, partialSymbol, concreteSymbol); | 27 | super(adapter, partialSymbol, concreteSymbol); |
27 | this.sourceType = sourceType; | 28 | this.sourceType = sourceType; |
28 | } | 29 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java new file mode 100644 index 00000000..198bdbfb --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.predicate; | ||
7 | |||
8 | import tools.refinery.logic.dnf.AbstractQueryBuilder; | ||
9 | import tools.refinery.logic.dnf.Query; | ||
10 | import tools.refinery.logic.literal.Literal; | ||
11 | import tools.refinery.logic.term.NodeVariable; | ||
12 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
13 | import tools.refinery.store.dse.propagation.PropagationBuilder; | ||
14 | import tools.refinery.store.dse.transition.Rule; | ||
15 | import tools.refinery.store.model.ModelStoreBuilder; | ||
16 | import tools.refinery.store.model.ModelStoreConfiguration; | ||
17 | import tools.refinery.store.query.view.ForbiddenView; | ||
18 | import tools.refinery.store.reasoning.lifting.DnfLifter; | ||
19 | import tools.refinery.store.reasoning.literal.Concreteness; | ||
20 | import tools.refinery.store.reasoning.literal.Modality; | ||
21 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
22 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | ||
23 | import tools.refinery.store.reasoning.translator.RoundingMode; | ||
24 | import tools.refinery.store.reasoning.translator.TranslationException; | ||
25 | import tools.refinery.store.representation.Symbol; | ||
26 | |||
27 | import java.util.ArrayList; | ||
28 | import java.util.List; | ||
29 | |||
30 | import static tools.refinery.logic.literal.Literals.not; | ||
31 | import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; | ||
32 | import static tools.refinery.store.reasoning.literal.PartialLiterals.*; | ||
33 | import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; | ||
34 | |||
35 | public class BasePredicateTranslator implements ModelStoreConfiguration { | ||
36 | private final PartialRelation predicate; | ||
37 | private final List<PartialRelation> parameterTypes; | ||
38 | private final TruthValue defaultValue; | ||
39 | private final boolean partial; | ||
40 | private final Symbol<TruthValue> symbol; | ||
41 | |||
42 | public BasePredicateTranslator(PartialRelation predicate, List<PartialRelation> parameterTypes, | ||
43 | TruthValue defaultValue, boolean partial) { | ||
44 | this.predicate = predicate; | ||
45 | this.parameterTypes = parameterTypes; | ||
46 | this.defaultValue = defaultValue; | ||
47 | this.partial = partial; | ||
48 | symbol = Symbol.of(predicate.name(), predicate.arity(), TruthValue.class, defaultValue); | ||
49 | } | ||
50 | |||
51 | @Override | ||
52 | public void apply(ModelStoreBuilder storeBuilder) { | ||
53 | int arity = predicate.arity(); | ||
54 | if (arity != parameterTypes.size()) { | ||
55 | throw new TranslationException(predicate, | ||
56 | "Expected %d parameter type for base predicate %s, got %d instead" | ||
57 | .formatted(arity, predicate, parameterTypes.size())); | ||
58 | } | ||
59 | if (defaultValue.must()) { | ||
60 | throw new TranslationException(predicate, "Unsupported default value %s for base predicate %s" | ||
61 | .formatted(defaultValue, predicate)); | ||
62 | } | ||
63 | var translator = PartialRelationTranslator.of(predicate); | ||
64 | translator.symbol(symbol); | ||
65 | if (defaultValue.may()) { | ||
66 | configureWithDefaultUnknown(translator); | ||
67 | } else { | ||
68 | configureWithDefaultFalse(storeBuilder); | ||
69 | } | ||
70 | translator.refiner(PredicateRefiner.of(symbol, parameterTypes)); | ||
71 | if (partial) { | ||
72 | translator.roundingMode(RoundingMode.NONE); | ||
73 | } else { | ||
74 | translator.decision(Rule.of(predicate.name(), builder -> { | ||
75 | var parameters = createParameters(builder); | ||
76 | var literals = new ArrayList<Literal>(arity + 2); | ||
77 | literals.add(may(predicate.call(parameters))); | ||
78 | literals.add(not(candidateMust(predicate.call(parameters)))); | ||
79 | for (int i = 0; i < arity; i++) { | ||
80 | literals.add(not(MULTI_VIEW.call(parameters[i]))); | ||
81 | } | ||
82 | builder.clause(literals); | ||
83 | builder.action(add(predicate, parameters)); | ||
84 | })); | ||
85 | } | ||
86 | storeBuilder.with(translator); | ||
87 | } | ||
88 | |||
89 | private NodeVariable[] createParameters(AbstractQueryBuilder<?> builder) { | ||
90 | int arity = predicate.arity(); | ||
91 | var parameters = new NodeVariable[arity]; | ||
92 | for (int i = 0; i < arity; i++) { | ||
93 | parameters[i] = builder.parameter("p" + (i + 1)); | ||
94 | } | ||
95 | return parameters; | ||
96 | } | ||
97 | |||
98 | private void configureWithDefaultUnknown(PartialRelationTranslator translator) { | ||
99 | var name = predicate.name(); | ||
100 | var mayName = DnfLifter.decorateName(name, Modality.MAY, Concreteness.PARTIAL); | ||
101 | int arity = predicate.arity(); | ||
102 | var forbiddenView = new ForbiddenView(symbol); | ||
103 | translator.may(Query.of(mayName, builder -> { | ||
104 | var parameters = createParameters(builder); | ||
105 | var literals = new ArrayList<Literal>(arity + 1); | ||
106 | for (int i = 0; i < arity; i++) { | ||
107 | literals.add(may(parameterTypes.get(i).call(parameters[i]))); | ||
108 | } | ||
109 | literals.add(not(forbiddenView.call(parameters))); | ||
110 | builder.clause(literals); | ||
111 | })); | ||
112 | if (partial) { | ||
113 | var candidateMayName = DnfLifter.decorateName(name, Modality.MAY, Concreteness.CANDIDATE); | ||
114 | translator.candidateMay(Query.of(candidateMayName, builder -> { | ||
115 | var parameters = createParameters(builder); | ||
116 | var literals = new ArrayList<Literal>(arity + 1); | ||
117 | for (int i = 0; i < arity; i++) { | ||
118 | literals.add(candidateMay(parameterTypes.get(i).call(parameters[i]))); | ||
119 | } | ||
120 | literals.add(not(forbiddenView.call(parameters))); | ||
121 | builder.clause(literals); | ||
122 | })); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | private void configureWithDefaultFalse(ModelStoreBuilder storeBuilder) { | ||
127 | var name = predicate.name(); | ||
128 | // Fail if there is no {@link PropagationBuilder}, since it is required for soundness. | ||
129 | var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class); | ||
130 | propagationBuilder.rule(Rule.of(name + "#invalidLink", builder -> { | ||
131 | var parameters = createParameters(builder); | ||
132 | int arity = parameters.length; | ||
133 | for (int i = 0; i < arity; i++) { | ||
134 | builder.clause( | ||
135 | may(predicate.call(parameters)), | ||
136 | not(may(parameterTypes.get(i).call(parameters[i]))) | ||
137 | ); | ||
138 | } | ||
139 | })); | ||
140 | } | ||
141 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java new file mode 100644 index 00000000..0fccced2 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.translator.predicate; | ||
7 | |||
8 | import org.jetbrains.annotations.Nullable; | ||
9 | import tools.refinery.logic.term.truthvalue.TruthValue; | ||
10 | import tools.refinery.store.reasoning.ReasoningAdapter; | ||
11 | import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; | ||
12 | import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; | ||
13 | import tools.refinery.store.reasoning.representation.PartialRelation; | ||
14 | import tools.refinery.store.reasoning.representation.PartialSymbol; | ||
15 | import tools.refinery.store.reasoning.seed.ModelSeed; | ||
16 | import tools.refinery.store.representation.Symbol; | ||
17 | import tools.refinery.store.tuple.Tuple; | ||
18 | |||
19 | import java.util.List; | ||
20 | import java.util.Objects; | ||
21 | |||
22 | class PredicateRefiner extends ConcreteSymbolRefiner<TruthValue, Boolean> { | ||
23 | private final List<PartialRelation> parameterTypes; | ||
24 | private @Nullable PartialInterpretationRefiner<TruthValue, Boolean>[] parameterTypeRefiners; | ||
25 | |||
26 | protected PredicateRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol, | ||
27 | Symbol<TruthValue> concreteSymbol, List<PartialRelation> parameterTypes) { | ||
28 | super(adapter, partialSymbol, concreteSymbol); | ||
29 | this.parameterTypes = parameterTypes; | ||
30 | } | ||
31 | |||
32 | @Override | ||
33 | public void afterCreate() { | ||
34 | int arity = parameterTypes.size(); | ||
35 | // Generic array creation. | ||
36 | @SuppressWarnings("unchecked") | ||
37 | PartialInterpretationRefiner<TruthValue, Boolean>[] array = new PartialInterpretationRefiner[arity]; | ||
38 | parameterTypeRefiners = array; | ||
39 | var adapter = getAdapter(); | ||
40 | for (int i = 0; i < arity; i++) { | ||
41 | var parameterType = parameterTypes.get(i); | ||
42 | if (parameterType != null) { | ||
43 | array[i] = adapter.getRefiner(parameterType); | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public boolean merge(Tuple key, TruthValue value) { | ||
50 | var currentValue = get(key); | ||
51 | var mergedValue = currentValue.meet(value); | ||
52 | if (!Objects.equals(currentValue, mergedValue)) { | ||
53 | put(key, mergedValue); | ||
54 | } | ||
55 | // Avoid cyclic propagation between parameter types by avoiding propagation after reaching a fixed point. | ||
56 | if (mergedValue.must() && !currentValue.must()) { | ||
57 | return refineParameters(key); | ||
58 | } | ||
59 | return true; | ||
60 | } | ||
61 | |||
62 | @Override | ||
63 | public void afterInitialize(ModelSeed modelSeed) { | ||
64 | var predicate = getPartialSymbol(); | ||
65 | var cursor = modelSeed.getCursor(predicate); | ||
66 | while (cursor.move()) { | ||
67 | var value = cursor.getValue(); | ||
68 | if (value.must()) { | ||
69 | var key = cursor.getKey(); | ||
70 | if (!refineParameters(key)) { | ||
71 | throw new IllegalArgumentException("Failed to merge parameter types of predicate %s for key %s" | ||
72 | .formatted(predicate, key)); | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | private boolean refineParameters(Tuple key) { | ||
79 | int arity = parameterTypeRefiners.length; | ||
80 | for (int i = 0; i < arity; i++) { | ||
81 | var refiner = parameterTypeRefiners[i]; | ||
82 | if (refiner != null && !refiner.merge(Tuple.of(key.get(i)), TruthValue.TRUE)) { | ||
83 | return false; | ||
84 | } | ||
85 | } | ||
86 | return true; | ||
87 | } | ||
88 | |||
89 | public static Factory<TruthValue, Boolean> of(Symbol<TruthValue> concreteSymbol, | ||
90 | List<PartialRelation> parameterTypes) { | ||
91 | return (adapter, partialSymbol) -> new PredicateRefiner(adapter, partialSymbol, concreteSymbol, | ||
92 | parameterTypes); | ||
93 | } | ||
94 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java index 010ce977..8dd9cff6 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java | |||
@@ -21,6 +21,9 @@ import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | |||
21 | import tools.refinery.store.reasoning.translator.TranslationException; | 21 | import tools.refinery.store.reasoning.translator.TranslationException; |
22 | import tools.refinery.store.representation.Symbol; | 22 | import tools.refinery.store.representation.Symbol; |
23 | 23 | ||
24 | import java.util.List; | ||
25 | import java.util.Objects; | ||
26 | |||
24 | import static tools.refinery.logic.literal.Literals.not; | 27 | import static tools.refinery.logic.literal.Literals.not; |
25 | import static tools.refinery.store.reasoning.literal.PartialLiterals.may; | 28 | import static tools.refinery.store.reasoning.literal.PartialLiterals.may; |
26 | import static tools.refinery.store.reasoning.literal.PartialLiterals.must; | 29 | import static tools.refinery.store.reasoning.literal.PartialLiterals.must; |
@@ -30,9 +33,11 @@ public class PredicateTranslator implements ModelStoreConfiguration { | |||
30 | private final RelationalQuery query; | 33 | private final RelationalQuery query; |
31 | private final boolean mutable; | 34 | private final boolean mutable; |
32 | private final TruthValue defaultValue; | 35 | private final TruthValue defaultValue; |
36 | private final List<PartialRelation> parameterTypes; | ||
33 | 37 | ||
34 | public PredicateTranslator(PartialRelation relation, RelationalQuery query, boolean mutable, | 38 | public PredicateTranslator(PartialRelation relation, RelationalQuery query, List<PartialRelation> parameterTypes, |
35 | TruthValue defaultValue) { | 39 | boolean mutable, TruthValue defaultValue) { |
40 | this.parameterTypes = parameterTypes; | ||
36 | if (relation.arity() != query.arity()) { | 41 | if (relation.arity() != query.arity()) { |
37 | throw new TranslationException(relation, "Expected arity %d query for partial relation %s, got %d instead" | 42 | throw new TranslationException(relation, "Expected arity %d query for partial relation %s, got %d instead" |
38 | .formatted(relation.arity(), relation, query.arity())); | 43 | .formatted(relation.arity(), relation, query.arity())); |
@@ -78,6 +83,10 @@ public class PredicateTranslator implements ModelStoreConfiguration { | |||
78 | .clause(mayLiterals) | 83 | .clause(mayLiterals) |
79 | .build(); | 84 | .build(); |
80 | translator.may(may); | 85 | translator.may(may); |
86 | |||
87 | if (parameterTypes != null && parameterTypes.stream().anyMatch(Objects::nonNull)) { | ||
88 | translator.refiner(PredicateRefiner.of(symbol, parameterTypes)); | ||
89 | } | ||
81 | } else if (defaultValue.may()) { | 90 | } else if (defaultValue.may()) { |
82 | // If all values are permitted, we don't need to check for any forbidden values in the model. | 91 | // If all values are permitted, we don't need to check for any forbidden values in the model. |
83 | // If the result of this predicate of {@code ERROR}, some other partial relation (that we check for) | 92 | // If the result of this predicate of {@code ERROR}, some other partial relation (that we check for) |