diff options
author | Kristóf Marussy <kristof@marussy.com> | 2024-07-03 01:58:03 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2024-07-03 21:15:40 +0200 |
commit | b63a2f7b2be7359c6971f8a20a9513614d9c287d (patch) | |
tree | 4bc2528a0d24f55824cbf77f70868f2a2697b0a6 /subprojects/store-reasoning | |
parent | feat: container type propagation (diff) | |
download | refinery-b63a2f7b2be7359c6971f8a20a9513614d9c287d.tar.gz refinery-b63a2f7b2be7359c6971f8a20a9513614d9c287d.tar.zst refinery-b63a2f7b2be7359c6971f8a20a9513614d9c287d.zip |
refactor(reasoning): lower multiplicity propagator
Diffstat (limited to 'subprojects/store-reasoning')
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; | |||
30 | import tools.refinery.store.reasoning.representation.PartialRelation; | 30 | import tools.refinery.store.reasoning.representation.PartialRelation; |
31 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | 31 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; |
32 | import tools.refinery.store.reasoning.translator.RoundingMode; | 32 | import tools.refinery.store.reasoning.translator.RoundingMode; |
33 | import tools.refinery.store.reasoning.translator.crossreference.CrossReferenceUtils; | ||
34 | import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; | 33 | import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; |
35 | import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; | 34 | import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; |
36 | import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; | 35 | import 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; | |||
12 | import tools.refinery.logic.term.NodeVariable; | 12 | import tools.refinery.logic.term.NodeVariable; |
13 | import tools.refinery.logic.term.Variable; | 13 | import tools.refinery.logic.term.Variable; |
14 | import tools.refinery.logic.term.uppercardinality.FiniteUpperCardinality; | 14 | import tools.refinery.logic.term.uppercardinality.FiniteUpperCardinality; |
15 | import tools.refinery.logic.term.uppercardinality.UpperCardinalities; | ||
16 | import tools.refinery.logic.term.uppercardinality.UpperCardinality; | ||
17 | import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms; | ||
18 | import tools.refinery.store.dse.propagation.PropagationBuilder; | ||
19 | import tools.refinery.store.dse.transition.Rule; | ||
20 | import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral; | 15 | import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral; |
21 | import tools.refinery.store.reasoning.literal.CountLowerBoundLiteral; | 16 | import tools.refinery.store.reasoning.literal.CountLowerBoundLiteral; |
22 | import tools.refinery.store.reasoning.literal.CountUpperBoundLiteral; | ||
23 | import tools.refinery.store.reasoning.representation.PartialRelation; | 17 | import tools.refinery.store.reasoning.representation.PartialRelation; |
24 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; | 18 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; |
25 | 19 | ||
@@ -27,14 +21,12 @@ import java.util.ArrayList; | |||
27 | import java.util.List; | 21 | import java.util.List; |
28 | 22 | ||
29 | import static tools.refinery.logic.literal.Literals.check; | 23 | import static tools.refinery.logic.literal.Literals.check; |
30 | import static tools.refinery.logic.literal.Literals.not; | ||
31 | import static tools.refinery.logic.term.int_.IntTerms.constant; | 24 | import static tools.refinery.logic.term.int_.IntTerms.constant; |
32 | import static tools.refinery.logic.term.int_.IntTerms.less; | 25 | import static tools.refinery.logic.term.int_.IntTerms.less; |
33 | import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; | 26 | import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMay; |
34 | import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove; | 27 | import static tools.refinery.store.reasoning.literal.PartialLiterals.may; |
35 | import static tools.refinery.store.reasoning.literal.PartialLiterals.*; | ||
36 | 28 | ||
37 | public class CrossReferenceUtils { | 29 | class 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; | |||
20 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | 20 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; |
21 | import tools.refinery.store.reasoning.translator.RoundingMode; | 21 | import tools.refinery.store.reasoning.translator.RoundingMode; |
22 | import tools.refinery.store.reasoning.translator.TranslationException; | 22 | import tools.refinery.store.reasoning.translator.TranslationException; |
23 | import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; | ||
24 | import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; | 23 | import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; |
25 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; | 24 | import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; |
26 | import tools.refinery.store.representation.Symbol; | 25 | import 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; | |||
19 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; | 19 | import tools.refinery.store.reasoning.translator.PartialRelationTranslator; |
20 | import tools.refinery.store.reasoning.translator.RoundingMode; | 20 | import tools.refinery.store.reasoning.translator.RoundingMode; |
21 | import tools.refinery.store.reasoning.translator.TranslationException; | 21 | import tools.refinery.store.reasoning.translator.TranslationException; |
22 | import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; | ||
23 | import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; | 22 | import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; |
24 | import tools.refinery.store.representation.Symbol; | 23 | import 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; | |||
11 | import tools.refinery.logic.term.uppercardinality.UpperCardinalities; | 11 | import tools.refinery.logic.term.uppercardinality.UpperCardinalities; |
12 | import tools.refinery.logic.term.uppercardinality.UpperCardinality; | 12 | import tools.refinery.logic.term.uppercardinality.UpperCardinality; |
13 | import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms; | 13 | import tools.refinery.logic.term.uppercardinality.UpperCardinalityTerms; |
14 | import tools.refinery.store.dse.propagation.PropagationBuilder; | ||
15 | import tools.refinery.store.dse.transition.Rule; | ||
14 | import tools.refinery.store.dse.transition.objectives.Objectives; | 16 | import tools.refinery.store.dse.transition.objectives.Objectives; |
15 | import tools.refinery.store.model.ModelStoreBuilder; | 17 | import tools.refinery.store.model.ModelStoreBuilder; |
16 | import tools.refinery.store.model.ModelStoreConfiguration; | 18 | import tools.refinery.store.model.ModelStoreConfiguration; |
@@ -24,9 +26,12 @@ import tools.refinery.store.reasoning.translator.TranslationException; | |||
24 | import java.util.List; | 26 | import java.util.List; |
25 | 27 | ||
26 | import static tools.refinery.logic.literal.Literals.check; | 28 | import static tools.refinery.logic.literal.Literals.check; |
29 | import static tools.refinery.logic.literal.Literals.not; | ||
27 | import static tools.refinery.logic.term.int_.IntTerms.*; | 30 | import static tools.refinery.logic.term.int_.IntTerms.*; |
28 | import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMust; | 31 | import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; |
29 | import static tools.refinery.store.reasoning.literal.PartialLiterals.must; | 32 | import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove; |
33 | import static tools.refinery.store.reasoning.literal.PartialLiterals.*; | ||
34 | import static tools.refinery.store.reasoning.literal.PartialLiterals.may; | ||
30 | 35 | ||
31 | public class InvalidMultiplicityErrorTranslator implements ModelStoreConfiguration { | 36 | public 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 | } |