aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-07-03 01:58:03 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-07-03 21:15:40 +0200
commitb63a2f7b2be7359c6971f8a20a9513614d9c287d (patch)
tree4bc2528a0d24f55824cbf77f70868f2a2697b0a6 /subprojects/store-reasoning
parentfeat: container type propagation (diff)
downloadrefinery-b63a2f7b2be7359c6971f8a20a9513614d9c287d.tar.gz
refinery-b63a2f7b2be7359c6971f8a20a9513614d9c287d.tar.zst
refinery-b63a2f7b2be7359c6971f8a20a9513614d9c287d.zip
refactor(reasoning): lower multiplicity propagator
Diffstat (limited to 'subprojects/store-reasoning')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java24
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/CrossReferenceUtils.java80
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java70
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java46
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java54
5 files changed, 99 insertions, 175 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java
index b15a2576..03a25747 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java
@@ -30,7 +30,6 @@ import tools.refinery.store.reasoning.refinement.RefinementBasedInitializer;
30import tools.refinery.store.reasoning.representation.PartialRelation; 30import tools.refinery.store.reasoning.representation.PartialRelation;
31import tools.refinery.store.reasoning.translator.PartialRelationTranslator; 31import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
32import tools.refinery.store.reasoning.translator.RoundingMode; 32import tools.refinery.store.reasoning.translator.RoundingMode;
33import tools.refinery.store.reasoning.translator.crossreference.CrossReferenceUtils;
34import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; 33import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator;
35import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; 34import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity;
36import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; 35import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator;
@@ -113,7 +112,7 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration {
113 translateInvalidMultiplicity(storeBuilder, linkType, info); 112 translateInvalidMultiplicity(storeBuilder, linkType, info);
114 } 113 }
115 translateFocusNotContained(storeBuilder); 114 translateFocusNotContained(storeBuilder);
116 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configurePropagationBuilder); 115 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configureSingleContainerPropagator);
117 } 116 }
118 117
119 private void translateContainmentLinkType(ModelStoreBuilder storeBuilder, PartialRelation linkType, 118 private void translateContainmentLinkType(ModelStoreBuilder storeBuilder, PartialRelation linkType,
@@ -268,27 +267,6 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration {
268 ))); 267 )));
269 } 268 }
270 269
271 private void configurePropagationBuilder(PropagationBuilder propagationBuilder) {
272 configureLowerMultiplicityPropagator(propagationBuilder);
273 configureSingleContainerPropagator(propagationBuilder);
274 }
275
276 private void configureLowerMultiplicityPropagator(PropagationBuilder propagationBuilder) {
277 for (var entry : containmentInfoMap.entrySet()) {
278 var info = entry.getValue();
279 if (info.multiplicity() instanceof ConstrainedMultiplicity constrainedMultiplicity) {
280 int lowerBound = constrainedMultiplicity.multiplicity().lowerBound();
281 if (lowerBound >= 1) {
282 var linkType = entry.getKey();
283 var sourceType = info.sourceType();
284 CrossReferenceUtils.configureSourceLowerBound(linkType, sourceType, lowerBound,
285 propagationBuilder);
286 }
287 }
288 }
289 CrossReferenceUtils.configureTargetLowerBound(CONTAINS_SYMBOL, CONTAINED_SYMBOL, 1, propagationBuilder);
290 }
291
292 private void configureSingleContainerPropagator(PropagationBuilder propagationBuilder) { 270 private void configureSingleContainerPropagator(PropagationBuilder propagationBuilder) {
293 var possibleContainment = Dnf.of(CONTAINS_SYMBOL.name() + "#possible", builder -> { 271 var possibleContainment = Dnf.of(CONTAINS_SYMBOL.name() + "#possible", builder -> {
294 var p1 = builder.parameter("source"); 272 var p1 = builder.parameter("source");
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/CrossReferenceUtils.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/CrossReferenceUtils.java
index b322513d..56c9d682 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/CrossReferenceUtils.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/CrossReferenceUtils.java
@@ -12,14 +12,8 @@ import tools.refinery.logic.literal.Literal;
12import tools.refinery.logic.term.NodeVariable; 12import tools.refinery.logic.term.NodeVariable;
13import tools.refinery.logic.term.Variable; 13import tools.refinery.logic.term.Variable;
14import tools.refinery.logic.term.uppercardinality.FiniteUpperCardinality; 14import tools.refinery.logic.term.uppercardinality.FiniteUpperCardinality;
15import tools.refinery.logic.term.uppercardinality.UpperCardinalities;
16import tools.refinery.logic.term.uppercardinality.UpperCardinality;
17import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms;
18import tools.refinery.store.dse.propagation.PropagationBuilder;
19import tools.refinery.store.dse.transition.Rule;
20import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral; 15import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral;
21import tools.refinery.store.reasoning.literal.CountLowerBoundLiteral; 16import tools.refinery.store.reasoning.literal.CountLowerBoundLiteral;
22import tools.refinery.store.reasoning.literal.CountUpperBoundLiteral;
23import tools.refinery.store.reasoning.representation.PartialRelation; 17import tools.refinery.store.reasoning.representation.PartialRelation;
24import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; 18import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity;
25 19
@@ -27,14 +21,12 @@ import java.util.ArrayList;
27import java.util.List; 21import java.util.List;
28 22
29import static tools.refinery.logic.literal.Literals.check; 23import static tools.refinery.logic.literal.Literals.check;
30import static tools.refinery.logic.literal.Literals.not;
31import static tools.refinery.logic.term.int_.IntTerms.constant; 24import static tools.refinery.logic.term.int_.IntTerms.constant;
32import static tools.refinery.logic.term.int_.IntTerms.less; 25import static tools.refinery.logic.term.int_.IntTerms.less;
33import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; 26import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMay;
34import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove; 27import static tools.refinery.store.reasoning.literal.PartialLiterals.may;
35import static tools.refinery.store.reasoning.literal.PartialLiterals.*;
36 28
37public class CrossReferenceUtils { 29class CrossReferenceUtils {
38 private CrossReferenceUtils() { 30 private CrossReferenceUtils() {
39 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 31 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
40 } 32 }
@@ -85,70 +77,4 @@ public class CrossReferenceUtils {
85 builder.parameter(variable); 77 builder.parameter(variable);
86 return new PreparedBuilder(builder, variable, arguments); 78 return new PreparedBuilder(builder, variable, arguments);
87 } 79 }
88
89 public static void configureSourceLowerBound(PartialRelation linkType, PartialRelation sourceType,
90 int lowerBound, PropagationBuilder propagationBuilder) {
91 var name = linkType.name();
92 var lowerBoundCardinality = UpperCardinalities.atMost(lowerBound);
93 propagationBuilder.rule(Rule.of(name + "#lowerSource", (builder, p1, p2) -> builder
94 .clause(UpperCardinality.class, upperBound -> List.of(
95 must(sourceType.call(p1)),
96 new CountUpperBoundLiteral(upperBound, linkType, List.of(p1, Variable.of())),
97 check(UpperCardinalityTerms.lessEq(upperBound,
98 UpperCardinalityTerms.constant(lowerBoundCardinality))),
99 may(linkType.call(p1, p2)),
100 not(must(linkType.call(p1, p2)))
101 ))
102 .action(
103 add(linkType, p1, p2)
104 )
105 ));
106 propagationBuilder.rule(Rule.of(name + "#missingTarget", (builder, p1) -> builder
107 .clause(UpperCardinality.class, upperBound -> List.of(
108 may(sourceType.call(p1)),
109 // Violation of monotonicity: stop the propagation of inconsistencies, since the
110 // {@code invalidMultiplicity} pattern will already mark the model as invalid.
111 not(must(sourceType.call(p1))),
112 new CountUpperBoundLiteral(upperBound, linkType, List.of(p1, Variable.of())),
113 check(UpperCardinalityTerms.less(upperBound,
114 UpperCardinalityTerms.constant(lowerBoundCardinality)))
115 ))
116 .action(
117 remove(sourceType, p1)
118 )
119 ));
120 }
121
122 public static void configureTargetLowerBound(PartialRelation linkType, PartialRelation targetType,
123 int lowerBound, PropagationBuilder propagationBuilder) {
124 var name = linkType.name();
125 var lowerBoundCardinality = UpperCardinalities.atMost(lowerBound);
126 propagationBuilder.rule(Rule.of(name + "#lowerTarget", (builder, p1, p2) -> builder
127 .clause(UpperCardinality.class, upperBound -> List.of(
128 must(targetType.call(p2)),
129 new CountUpperBoundLiteral(upperBound, linkType, List.of(Variable.of(), p2)),
130 check(UpperCardinalityTerms.lessEq(upperBound,
131 UpperCardinalityTerms.constant(lowerBoundCardinality))),
132 may(linkType.call(p1, p2)),
133 not(must(linkType.call(p1, p2)))
134 ))
135 .action(
136 add(linkType, p1, p2)
137 )
138 ));
139 propagationBuilder.rule(Rule.of(name + "#missingSource", (builder, p1) -> builder
140 .clause(UpperCardinality.class, upperBound -> List.of(
141 may(targetType.call(p1)),
142 // Violation of monotonicity: stop the propagation of inconsistencies, since the
143 // {@code invalidMultiplicity} pattern will already mark the model as invalid.
144 not(must(targetType.call(p1))),
145 new CountUpperBoundLiteral(upperBound, linkType, List.of(Variable.of(), p1)),
146 check(UpperCardinalityTerms.less(upperBound,
147 UpperCardinalityTerms.constant(lowerBoundCardinality)))
148 ))
149 .action(
150 remove(targetType, p1)
151 )
152 ));
153 }
154} 80}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java
index 2facb56d..d4c2afd2 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java
@@ -20,7 +20,6 @@ import tools.refinery.store.reasoning.representation.PartialRelation;
20import tools.refinery.store.reasoning.translator.PartialRelationTranslator; 20import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
21import tools.refinery.store.reasoning.translator.RoundingMode; 21import tools.refinery.store.reasoning.translator.RoundingMode;
22import tools.refinery.store.reasoning.translator.TranslationException; 22import tools.refinery.store.reasoning.translator.TranslationException;
23import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity;
24import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; 23import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator;
25import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; 24import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity;
26import tools.refinery.store.representation.Symbol; 25import tools.refinery.store.representation.Symbol;
@@ -79,7 +78,6 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration
79 info.sourceMultiplicity())); 78 info.sourceMultiplicity()));
80 storeBuilder.with(new InvalidMultiplicityErrorTranslator(targetType, linkType, true, 79 storeBuilder.with(new InvalidMultiplicityErrorTranslator(targetType, linkType, true,
81 info.targetMultiplicity())); 80 info.targetMultiplicity()));
82 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configureLowerMultiplicityPropagator);
83 } 81 }
84 82
85 private void configureWithDefaultUnknown(PartialRelationTranslator translator) { 83 private void configureWithDefaultUnknown(PartialRelationTranslator translator) {
@@ -148,48 +146,32 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration
148 var targetType = info.targetType(); 146 var targetType = info.targetType();
149 var mayNewSource = createMayHelper(sourceType, info.sourceMultiplicity(), false); 147 var mayNewSource = createMayHelper(sourceType, info.sourceMultiplicity(), false);
150 var mayNewTarget = createMayHelper(targetType, info.targetMultiplicity(), true); 148 var mayNewTarget = createMayHelper(targetType, info.targetMultiplicity(), true);
151 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(propagationBuilder -> propagationBuilder 149 // Fail if there is no {@link PropagationBuilder}, since it is required for soundness.
152 .rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> { 150 var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class);
153 builder.clause( 151 propagationBuilder.rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> {
154 may(linkType.call(p1, p2)), 152 builder.clause(
155 not(may(sourceType.call(p1))) 153 may(linkType.call(p1, p2)),
156 ); 154 not(may(sourceType.call(p1)))
157 builder.clause( 155 );
158 may(linkType.call(p1, p2)), 156 builder.clause(
159 not(may(targetType.call(p2))) 157 may(linkType.call(p1, p2)),
160 ); 158 not(may(targetType.call(p2)))
161 if (info.isConstrained()) { 159 );
162 builder.clause( 160 if (info.isConstrained()) {
163 may(linkType.call(p1, p2)), 161 builder.clause(
164 not(must(linkType.call(p1, p2))), 162 may(linkType.call(p1, p2)),
165 not(mayNewSource.call(p1)) 163 not(must(linkType.call(p1, p2))),
166 ); 164 not(mayNewSource.call(p1))
167 builder.clause( 165 );
168 may(linkType.call(p1, p2)), 166 builder.clause(
169 not(must(linkType.call(p1, p2))), 167 may(linkType.call(p1, p2)),
170 not(mayNewTarget.call(p2)) 168 not(must(linkType.call(p1, p2))),
171 ); 169 not(mayNewTarget.call(p2))
172 } 170 );
173 builder.action(
174 remove(linkType, p1, p2)
175 );
176 })));
177 }
178
179 private void configureLowerMultiplicityPropagator(PropagationBuilder propagationBuilder) {
180 if (info.sourceMultiplicity() instanceof ConstrainedMultiplicity constrainedMultiplicity) {
181 int lowerBound = constrainedMultiplicity.multiplicity().lowerBound();
182 if (lowerBound >= 1) {
183 var sourceType = info.sourceType();
184 CrossReferenceUtils.configureSourceLowerBound(linkType, sourceType, lowerBound, propagationBuilder);
185 }
186 }
187 if (info.targetMultiplicity() instanceof ConstrainedMultiplicity constrainedMultiplicity) {
188 int lowerBound = constrainedMultiplicity.multiplicity().lowerBound();
189 if (lowerBound >= 1) {
190 var targetType = info.targetType();
191 CrossReferenceUtils.configureTargetLowerBound(linkType, targetType, lowerBound, propagationBuilder);
192 } 171 }
193 } 172 builder.action(
173 remove(linkType, p1, p2)
174 );
175 }));
194 } 176 }
195} 177}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java
index 0a718033..494211fe 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java
@@ -19,7 +19,6 @@ import tools.refinery.store.reasoning.representation.PartialRelation;
19import tools.refinery.store.reasoning.translator.PartialRelationTranslator; 19import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
20import tools.refinery.store.reasoning.translator.RoundingMode; 20import tools.refinery.store.reasoning.translator.RoundingMode;
21import tools.refinery.store.reasoning.translator.TranslationException; 21import tools.refinery.store.reasoning.translator.TranslationException;
22import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity;
23import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; 22import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator;
24import tools.refinery.store.representation.Symbol; 23import tools.refinery.store.representation.Symbol;
25 24
@@ -73,7 +72,6 @@ public class UndirectedCrossReferenceTranslator implements ModelStoreConfigurati
73 } 72 }
74 storeBuilder.with(translator); 73 storeBuilder.with(translator);
75 storeBuilder.with(new InvalidMultiplicityErrorTranslator(type, linkType, false, info.multiplicity())); 74 storeBuilder.with(new InvalidMultiplicityErrorTranslator(type, linkType, false, info.multiplicity()));
76 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configureLowerMultiplicityPropagator);
77 } 75 }
78 76
79 private void configureWithDefaultUnknown(PartialRelationTranslator translator) { 77 private void configureWithDefaultUnknown(PartialRelationTranslator translator) {
@@ -127,33 +125,23 @@ public class UndirectedCrossReferenceTranslator implements ModelStoreConfigurati
127 var name = linkType.name(); 125 var name = linkType.name();
128 var type = info.type(); 126 var type = info.type();
129 var mayNewSource = CrossReferenceUtils.createMayHelper(linkType, type, info.multiplicity(), false); 127 var mayNewSource = CrossReferenceUtils.createMayHelper(linkType, type, info.multiplicity(), false);
130 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(propagationBuilder -> propagationBuilder 128 // Fail if there is no {@link PropagationBuilder}, since it is required for soundness.
131 .rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> { 129 var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class);
132 builder.clause( 130 propagationBuilder.rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> {
133 may(linkType.call(p1, p2)), 131 builder.clause(
134 not(may(type.call(p1))) 132 may(linkType.call(p1, p2)),
135 ); 133 not(may(type.call(p1)))
136 if (info.isConstrained()) { 134 );
137 builder.clause( 135 if (info.isConstrained()) {
138 may(linkType.call(p1, p2)), 136 builder.clause(
139 not(must(linkType.call(p1, p2))), 137 may(linkType.call(p1, p2)),
140 not(mayNewSource.call(p1)) 138 not(must(linkType.call(p1, p2))),
141 ); 139 not(mayNewSource.call(p1))
142 } 140 );
143 builder.action(
144 remove(linkType, p1, p2)
145 );
146 })));
147 }
148
149 private void configureLowerMultiplicityPropagator(PropagationBuilder propagationBuilder) {
150 var name = linkType.name();
151 if (info.multiplicity() instanceof ConstrainedMultiplicity constrainedMultiplicity) {
152 int lowerBound = constrainedMultiplicity.multiplicity().lowerBound();
153 if (lowerBound >= 1) {
154 var type = info.type();
155 CrossReferenceUtils.configureSourceLowerBound(linkType, type, lowerBound, propagationBuilder);
156 } 141 }
157 } 142 builder.action(
143 remove(linkType, p1, p2)
144 );
145 }));
158 } 146 }
159} 147}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java
index e2eff921..54e1e4cc 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java
@@ -11,6 +11,8 @@ import tools.refinery.logic.term.uppercardinality.FiniteUpperCardinality;
11import tools.refinery.logic.term.uppercardinality.UpperCardinalities; 11import tools.refinery.logic.term.uppercardinality.UpperCardinalities;
12import tools.refinery.logic.term.uppercardinality.UpperCardinality; 12import tools.refinery.logic.term.uppercardinality.UpperCardinality;
13import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms; 13import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms;
14import tools.refinery.store.dse.propagation.PropagationBuilder;
15import tools.refinery.store.dse.transition.Rule;
14import tools.refinery.store.dse.transition.objectives.Objectives; 16import tools.refinery.store.dse.transition.objectives.Objectives;
15import tools.refinery.store.model.ModelStoreBuilder; 17import tools.refinery.store.model.ModelStoreBuilder;
16import tools.refinery.store.model.ModelStoreConfiguration; 18import tools.refinery.store.model.ModelStoreConfiguration;
@@ -24,9 +26,12 @@ import tools.refinery.store.reasoning.translator.TranslationException;
24import java.util.List; 26import java.util.List;
25 27
26import static tools.refinery.logic.literal.Literals.check; 28import static tools.refinery.logic.literal.Literals.check;
29import static tools.refinery.logic.literal.Literals.not;
27import static tools.refinery.logic.term.int_.IntTerms.*; 30import static tools.refinery.logic.term.int_.IntTerms.*;
28import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMust; 31import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add;
29import static tools.refinery.store.reasoning.literal.PartialLiterals.must; 32import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove;
33import static tools.refinery.store.reasoning.literal.PartialLiterals.*;
34import static tools.refinery.store.reasoning.literal.PartialLiterals.may;
30 35
31public class InvalidMultiplicityErrorTranslator implements ModelStoreConfiguration { 36public class InvalidMultiplicityErrorTranslator implements ModelStoreConfiguration {
32 private final PartialRelation nodeType; 37 private final PartialRelation nodeType;
@@ -132,5 +137,50 @@ public class InvalidMultiplicityErrorTranslator implements ModelStoreConfigurati
132 .candidateMay(candidateMayBuilder.build()) 137 .candidateMay(candidateMayBuilder.build())
133 .candidateMust(candidateMustBuilder.build()) 138 .candidateMust(candidateMustBuilder.build())
134 .objective(Objectives.value(objective))); 139 .objective(Objectives.value(objective)));
140
141 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configureLowerMultiplicityPropagator);
142 }
143
144 private void configureLowerMultiplicityPropagator(PropagationBuilder propagationBuilder) {
145 if (!(multiplicity instanceof ConstrainedMultiplicity constrainedMultiplicity)) {
146 return;
147 }
148 int lowerBound = constrainedMultiplicity.multiplicity().lowerBound();
149 if (lowerBound < 1) {
150 return;
151 }
152 var name = linkType.name();
153 var lowerBoundCardinality = UpperCardinalities.atMost(lowerBound);
154 var lowerSuffix = inverse ? "#lowerTarget" : "#lowerSource";
155 propagationBuilder.rule(Rule.of(name + lowerSuffix, (builder, p1, p2) -> builder
156 .clause(UpperCardinality.class, upperBound -> List.of(
157 must(nodeType.call(inverse ? p2 : p1)),
158 new CountUpperBoundLiteral(upperBound, linkType,
159 inverse ? List.of(Variable.of(), p2) : List.of(p1, Variable.of())),
160 check(UpperCardinalityTerms.lessEq(upperBound,
161 UpperCardinalityTerms.constant(lowerBoundCardinality))),
162 may(linkType.call(p1, p2)),
163 not(must(linkType.call(p1, p2)))
164 ))
165 .action(
166 add(linkType, p1, p2)
167 )
168 ));
169 var missingSuffix = inverse ? "#missingSource" : "#missingTarget";
170 propagationBuilder.rule(Rule.of(name + missingSuffix, (builder, p1) -> builder
171 .clause(UpperCardinality.class, upperBound -> List.of(
172 may(nodeType.call(p1)),
173 // Violation of monotonicity: stop the propagation of inconsistencies, since the
174 // {@code invalidMultiplicity} pattern will already mark the model as invalid.
175 not(must(nodeType.call(p1))),
176 new CountUpperBoundLiteral(upperBound, linkType,
177 inverse ? List.of(Variable.of(), p1) : List.of(p1, Variable.of())),
178 check(UpperCardinalityTerms.less(upperBound,
179 UpperCardinalityTerms.constant(lowerBoundCardinality)))
180 ))
181 .action(
182 remove(nodeType, p1)
183 )
184 ));
135 } 185 }
136} 186}