aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2024-08-01 18:27:10 +0200
committerLibravatar Kristóf Marussy <marussy@mit.bme.hu>2024-08-01 18:27:10 +0200
commit228a54b5b4cf21a4dbf5e45fd50361cec3068f35 (patch)
treec7fe76c9b293886c20814b8e814e4b4c09270a54 /subprojects/store-reasoning
parentrefactor(reasoning): propagation in refiner (diff)
downloadrefinery-228a54b5b4cf21a4dbf5e45fd50361cec3068f35.tar.gz
refinery-228a54b5b4cf21a4dbf5e45fd50361cec3068f35.tar.zst
refinery-228a54b5b4cf21a4dbf5e45fd50361cec3068f35.zip
feat: base predicates
Diffstat (limited to 'subprojects/store-reasoning')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java6
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java5
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java141
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java94
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java13
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 */
6package tools.refinery.store.reasoning.translator.predicate;
7
8import tools.refinery.logic.dnf.AbstractQueryBuilder;
9import tools.refinery.logic.dnf.Query;
10import tools.refinery.logic.literal.Literal;
11import tools.refinery.logic.term.NodeVariable;
12import tools.refinery.logic.term.truthvalue.TruthValue;
13import tools.refinery.store.dse.propagation.PropagationBuilder;
14import tools.refinery.store.dse.transition.Rule;
15import tools.refinery.store.model.ModelStoreBuilder;
16import tools.refinery.store.model.ModelStoreConfiguration;
17import tools.refinery.store.query.view.ForbiddenView;
18import tools.refinery.store.reasoning.lifting.DnfLifter;
19import tools.refinery.store.reasoning.literal.Concreteness;
20import tools.refinery.store.reasoning.literal.Modality;
21import tools.refinery.store.reasoning.representation.PartialRelation;
22import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
23import tools.refinery.store.reasoning.translator.RoundingMode;
24import tools.refinery.store.reasoning.translator.TranslationException;
25import tools.refinery.store.representation.Symbol;
26
27import java.util.ArrayList;
28import java.util.List;
29
30import static tools.refinery.logic.literal.Literals.not;
31import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add;
32import static tools.refinery.store.reasoning.literal.PartialLiterals.*;
33import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW;
34
35public 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 */
6package tools.refinery.store.reasoning.translator.predicate;
7
8import org.jetbrains.annotations.Nullable;
9import tools.refinery.logic.term.truthvalue.TruthValue;
10import tools.refinery.store.reasoning.ReasoningAdapter;
11import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner;
12import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner;
13import tools.refinery.store.reasoning.representation.PartialRelation;
14import tools.refinery.store.reasoning.representation.PartialSymbol;
15import tools.refinery.store.reasoning.seed.ModelSeed;
16import tools.refinery.store.representation.Symbol;
17import tools.refinery.store.tuple.Tuple;
18
19import java.util.List;
20import java.util.Objects;
21
22class 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;
21import tools.refinery.store.reasoning.translator.TranslationException; 21import tools.refinery.store.reasoning.translator.TranslationException;
22import tools.refinery.store.representation.Symbol; 22import tools.refinery.store.representation.Symbol;
23 23
24import java.util.List;
25import java.util.Objects;
26
24import static tools.refinery.logic.literal.Literals.not; 27import static tools.refinery.logic.literal.Literals.not;
25import static tools.refinery.store.reasoning.literal.PartialLiterals.may; 28import static tools.refinery.store.reasoning.literal.PartialLiterals.may;
26import static tools.refinery.store.reasoning.literal.PartialLiterals.must; 29import 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)