aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-07-02 19:29:31 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-07-02 19:29:31 +0200
commitbcd0b7aee57d4a13b541e8be5a8100e568c81aac (patch)
tree6ab7391df7f0aca43b8f42be14031e2c8fb555f2
parentrefactor(language): rename definition to computed (diff)
downloadrefinery-bcd0b7aee57d4a13b541e8be5a8100e568c81aac.tar.gz
refinery-bcd0b7aee57d4a13b541e8be5a8100e568c81aac.tar.zst
refinery-bcd0b7aee57d4a13b541e8be5a8100e568c81aac.zip
feat(reasoning): lower bound propagationrules
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslator.java65
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/CrossReferenceUtils.java83
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java75
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java47
4 files changed, 218 insertions, 52 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 1183d456..c862bc33 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
@@ -5,14 +5,18 @@
5 */ 5 */
6package tools.refinery.store.reasoning.translator.containment; 6package tools.refinery.store.reasoning.translator.containment;
7 7
8import tools.refinery.logic.dnf.Dnf;
8import tools.refinery.logic.dnf.Query; 9import tools.refinery.logic.dnf.Query;
9import tools.refinery.logic.dnf.RelationalQuery; 10import tools.refinery.logic.dnf.RelationalQuery;
10import tools.refinery.logic.literal.Connectivity; 11import tools.refinery.logic.literal.Connectivity;
11import tools.refinery.logic.literal.Literal; 12import tools.refinery.logic.literal.Literal;
12import tools.refinery.logic.literal.RepresentativeElectionLiteral; 13import tools.refinery.logic.literal.RepresentativeElectionLiteral;
14import tools.refinery.logic.term.ConstantTerm;
13import tools.refinery.logic.term.Variable; 15import tools.refinery.logic.term.Variable;
14import tools.refinery.logic.term.cardinalityinterval.CardinalityIntervals; 16import tools.refinery.logic.term.cardinalityinterval.CardinalityIntervals;
17import tools.refinery.logic.term.int_.IntTerms;
15import tools.refinery.logic.term.uppercardinality.FiniteUpperCardinality; 18import tools.refinery.logic.term.uppercardinality.FiniteUpperCardinality;
19import tools.refinery.store.dse.propagation.PropagationBuilder;
16import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; 20import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder;
17import tools.refinery.store.dse.transition.Rule; 21import tools.refinery.store.dse.transition.Rule;
18import tools.refinery.store.model.ModelStoreBuilder; 22import tools.refinery.store.model.ModelStoreBuilder;
@@ -26,6 +30,7 @@ import tools.refinery.store.reasoning.refinement.RefinementBasedInitializer;
26import tools.refinery.store.reasoning.representation.PartialRelation; 30import tools.refinery.store.reasoning.representation.PartialRelation;
27import tools.refinery.store.reasoning.translator.PartialRelationTranslator; 31import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
28import tools.refinery.store.reasoning.translator.RoundingMode; 32import tools.refinery.store.reasoning.translator.RoundingMode;
33import tools.refinery.store.reasoning.translator.crossreference.CrossReferenceUtils;
29import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; 34import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator;
30import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; 35import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity;
31import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; 36import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator;
@@ -35,10 +40,10 @@ import java.util.ArrayList;
35import java.util.List; 40import java.util.List;
36import java.util.Map; 41import java.util.Map;
37 42
38import static tools.refinery.logic.term.int_.IntTerms.constant;
39import static tools.refinery.logic.term.int_.IntTerms.less;
40import static tools.refinery.logic.literal.Literals.check; 43import static tools.refinery.logic.literal.Literals.check;
41import static tools.refinery.logic.literal.Literals.not; 44import static tools.refinery.logic.literal.Literals.not;
45import static tools.refinery.logic.term.int_.IntTerms.constant;
46import static tools.refinery.logic.term.int_.IntTerms.less;
42import static tools.refinery.store.reasoning.ReasoningAdapter.EXISTS_SYMBOL; 47import static tools.refinery.store.reasoning.ReasoningAdapter.EXISTS_SYMBOL;
43import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; 48import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add;
44import static tools.refinery.store.reasoning.actions.PartialActionLiterals.focus; 49import static tools.refinery.store.reasoning.actions.PartialActionLiterals.focus;
@@ -107,6 +112,7 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration {
107 translateInvalidMultiplicity(storeBuilder, linkType, info); 112 translateInvalidMultiplicity(storeBuilder, linkType, info);
108 } 113 }
109 translateFocusNotContained(storeBuilder); 114 translateFocusNotContained(storeBuilder);
115 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configurePropagationBuilder);
110 } 116 }
111 117
112 private void translateContainmentLinkType(ModelStoreBuilder storeBuilder, PartialRelation linkType, 118 private void translateContainmentLinkType(ModelStoreBuilder storeBuilder, PartialRelation linkType,
@@ -119,10 +125,10 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration {
119 var mayNewSourceHelper = Query.of(name + "#mayNewSourceHelper", (builder, parent) -> { 125 var mayNewSourceHelper = Query.of(name + "#mayNewSourceHelper", (builder, parent) -> {
120 var literals = new ArrayList<Literal>(); 126 var literals = new ArrayList<Literal>();
121 literals.add(may(sourceType.call(parent))); 127 literals.add(may(sourceType.call(parent)));
122 if (upperCardinality instanceof FiniteUpperCardinality finiteUpperCardinality) { 128 if (upperCardinality instanceof FiniteUpperCardinality(var finiteUpperBound)) {
123 var existingCount = Variable.of("existingCount", Integer.class); 129 var existingCount = Variable.of("existingCount", Integer.class);
124 literals.add(new CountLowerBoundLiteral(existingCount, linkType, List.of(parent, Variable.of()))); 130 literals.add(new CountLowerBoundLiteral(existingCount, linkType, List.of(parent, Variable.of())));
125 literals.add(check(less(existingCount, constant(finiteUpperCardinality.finiteUpperBound())))); 131 literals.add(check(less(existingCount, constant(finiteUpperBound))));
126 } 132 }
127 builder.clause(literals); 133 builder.clause(literals);
128 }); 134 });
@@ -260,4 +266,55 @@ public class ContainmentHierarchyTranslator implements ModelStoreConfiguration {
260 focus(multi, Variable.of()) 266 focus(multi, Variable.of())
261 ))); 267 )));
262 } 268 }
269
270 private void configurePropagationBuilder(PropagationBuilder propagationBuilder) {
271 configureLowerMultiplicityPropagator(propagationBuilder);
272 configureSingleContainerPropagator(propagationBuilder);
273 }
274
275 private void configureLowerMultiplicityPropagator(PropagationBuilder propagationBuilder) {
276 for (var entry : containmentInfoMap.entrySet()) {
277 var info = entry.getValue();
278 if (info.multiplicity() instanceof ConstrainedMultiplicity constrainedMultiplicity) {
279 int lowerBound = constrainedMultiplicity.multiplicity().lowerBound();
280 if (lowerBound >= 1) {
281 var linkType = entry.getKey();
282 var sourceType = info.sourceType();
283 CrossReferenceUtils.configureSourceLowerBound(linkType, sourceType, lowerBound,
284 propagationBuilder);
285 }
286 }
287 }
288 CrossReferenceUtils.configureTargetLowerBound(CONTAINS_SYMBOL, CONTAINED_SYMBOL, 1, propagationBuilder);
289 }
290
291 private void configureSingleContainerPropagator(PropagationBuilder propagationBuilder) {
292 var possibleContainment = Dnf.of(CONTAINS_SYMBOL.name() + "#possible", builder -> {
293 var p1 = builder.parameter("source");
294 var p2 = builder.parameter("target");
295 var output = builder.parameter("containmentLink", PartialRelation.class);
296 for (var containmentLink : containmentInfoMap.keySet()) {
297 builder.clause(
298 must(CONTAINS_SYMBOL.call(p1, p2)),
299 may(containmentLink.call(p1, p2)),
300 output.assign(new ConstantTerm<>(PartialRelation.class, containmentLink))
301 );
302 }
303 });
304 for (var containmentLink : containmentInfoMap.keySet()) {
305 propagationBuilder.rule(Rule.of(containmentLink.name() + "#single", (builder, p1, p2) -> builder
306 .clause(Integer.class, containmentCount -> List.of(
307 must(CONTAINS_SYMBOL.call(p1, p2)),
308 may(containmentLink.call(p1, p2)),
309 not(must(containmentLink.call(p1, p2))),
310 containmentCount.assign(possibleContainment.count(p1, p2,
311 Variable.of(PartialRelation.class))),
312 check(IntTerms.eq(containmentCount, constant(1)))
313 ))
314 .action(
315 add(containmentLink, p1, p2)
316 )
317 ));
318 }
319 }
263} 320}
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 56c9d682..be9fcb9d 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,8 +12,14 @@ 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;
15import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral; 20import tools.refinery.store.reasoning.literal.CountCandidateLowerBoundLiteral;
16import tools.refinery.store.reasoning.literal.CountLowerBoundLiteral; 21import tools.refinery.store.reasoning.literal.CountLowerBoundLiteral;
22import tools.refinery.store.reasoning.literal.CountUpperBoundLiteral;
17import tools.refinery.store.reasoning.representation.PartialRelation; 23import tools.refinery.store.reasoning.representation.PartialRelation;
18import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; 24import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity;
19 25
@@ -21,12 +27,15 @@ import java.util.ArrayList;
21import java.util.List; 27import java.util.List;
22 28
23import static tools.refinery.logic.literal.Literals.check; 29import static tools.refinery.logic.literal.Literals.check;
30import static tools.refinery.logic.literal.Literals.not;
24import static tools.refinery.logic.term.int_.IntTerms.constant; 31import static tools.refinery.logic.term.int_.IntTerms.constant;
25import static tools.refinery.logic.term.int_.IntTerms.less; 32import static tools.refinery.logic.term.int_.IntTerms.less;
26import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMay; 33import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add;
27import static tools.refinery.store.reasoning.literal.PartialLiterals.may; 34import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove;
35import static tools.refinery.store.reasoning.literal.PartialLiterals.*;
36import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW;
28 37
29class CrossReferenceUtils { 38public class CrossReferenceUtils {
30 private CrossReferenceUtils() { 39 private CrossReferenceUtils() {
31 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 40 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
32 } 41 }
@@ -77,4 +86,72 @@ class CrossReferenceUtils {
77 builder.parameter(variable); 86 builder.parameter(variable);
78 return new PreparedBuilder(builder, variable, arguments); 87 return new PreparedBuilder(builder, variable, arguments);
79 } 88 }
89
90 public static void configureSourceLowerBound(PartialRelation linkType, PartialRelation sourceType,
91 int lowerBound, PropagationBuilder propagationBuilder) {
92 var name = linkType.name();
93 var lowerBoundCardinality = UpperCardinalities.atMost(lowerBound);
94 propagationBuilder.rule(Rule.of(name + "#lowerSource", (builder, p1, p2) -> builder
95 .clause(UpperCardinality.class, upperBound -> List.of(
96 not(MULTI_VIEW.call(p1)),
97 must(sourceType.call(p1)),
98 new CountUpperBoundLiteral(upperBound, linkType, List.of(p1, Variable.of())),
99 check(UpperCardinalityTerms.lessEq(upperBound,
100 UpperCardinalityTerms.constant(lowerBoundCardinality))),
101 may(linkType.call(p1, p2)),
102 not(must(linkType.call(p1, p2)))
103 ))
104 .action(
105 add(linkType, p1, p2)
106 )
107 ));
108 propagationBuilder.rule(Rule.of(name + "#missingTarget", (builder, p1) -> builder
109 .clause(UpperCardinality.class, upperBound -> List.of(
110 may(sourceType.call(p1)),
111 // Violation of monotonicity: stop the propagation of inconsistencies, since the
112 // {@code invalidMultiplicity} pattern will already mark the model as invalid.
113 not(must(sourceType.call(p1))),
114 new CountUpperBoundLiteral(upperBound, linkType, List.of(p1, Variable.of())),
115 check(UpperCardinalityTerms.less(upperBound,
116 UpperCardinalityTerms.constant(lowerBoundCardinality)))
117 ))
118 .action(
119 remove(sourceType, p1)
120 )
121 ));
122 }
123
124 public static void configureTargetLowerBound(PartialRelation linkType, PartialRelation targetType,
125 int lowerBound, PropagationBuilder propagationBuilder) {
126 var name = linkType.name();
127 var lowerBoundCardinality = UpperCardinalities.atMost(lowerBound);
128 propagationBuilder.rule(Rule.of(name + "#lowerTarget", (builder, p1, p2) -> builder
129 .clause(UpperCardinality.class, upperBound -> List.of(
130 not(MULTI_VIEW.call(p2)),
131 must(targetType.call(p2)),
132 new CountUpperBoundLiteral(upperBound, linkType, List.of(Variable.of(), p2)),
133 check(UpperCardinalityTerms.lessEq(upperBound,
134 UpperCardinalityTerms.constant(lowerBoundCardinality))),
135 may(linkType.call(p1, p2)),
136 not(must(linkType.call(p1, p2)))
137 ))
138 .action(
139 add(linkType, p1, p2)
140 )
141 ));
142 propagationBuilder.rule(Rule.of(name + "#missingSource", (builder, p1) -> builder
143 .clause(UpperCardinality.class, upperBound -> List.of(
144 may(targetType.call(p1)),
145 // Violation of monotonicity: stop the propagation of inconsistencies, since the
146 // {@code invalidMultiplicity} pattern will already mark the model as invalid.
147 not(must(targetType.call(p1))),
148 new CountUpperBoundLiteral(upperBound, linkType, List.of(Variable.of(), p1)),
149 check(UpperCardinalityTerms.less(upperBound,
150 UpperCardinalityTerms.constant(lowerBoundCardinality)))
151 ))
152 .action(
153 remove(targetType, p1)
154 )
155 ));
156 }
80} 157}
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 1985a43f..2facb56d 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,13 +20,14 @@ 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;
23import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; 24import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator;
24import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; 25import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity;
25import tools.refinery.store.representation.Symbol; 26import tools.refinery.store.representation.Symbol;
26 27
27import static tools.refinery.logic.literal.Literals.not; 28import static tools.refinery.logic.literal.Literals.not;
28import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; 29import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add;
29import static tools.refinery.store.reasoning.actions.PartialActionLiterals.merge; 30import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove;
30import static tools.refinery.store.reasoning.literal.PartialLiterals.*; 31import static tools.refinery.store.reasoning.literal.PartialLiterals.*;
31import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; 32import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW;
32 33
@@ -78,6 +79,7 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration
78 info.sourceMultiplicity())); 79 info.sourceMultiplicity()));
79 storeBuilder.with(new InvalidMultiplicityErrorTranslator(targetType, linkType, true, 80 storeBuilder.with(new InvalidMultiplicityErrorTranslator(targetType, linkType, true,
80 info.targetMultiplicity())); 81 info.targetMultiplicity()));
82 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configureLowerMultiplicityPropagator);
81 } 83 }
82 84
83 private void configureWithDefaultUnknown(PartialRelationTranslator translator) { 85 private void configureWithDefaultUnknown(PartialRelationTranslator translator) {
@@ -135,7 +137,8 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration
135 return CrossReferenceUtils.createMayHelper(linkType, type, multiplicity, inverse); 137 return CrossReferenceUtils.createMayHelper(linkType, type, multiplicity, inverse);
136 } 138 }
137 139
138 private RelationalQuery createCandidateMayHelper(PartialRelation type, Multiplicity multiplicity, boolean inverse) { 140 private RelationalQuery createCandidateMayHelper(PartialRelation type, Multiplicity multiplicity,
141 boolean inverse) {
139 return CrossReferenceUtils.createCandidateMayHelper(linkType, type, multiplicity, inverse); 142 return CrossReferenceUtils.createCandidateMayHelper(linkType, type, multiplicity, inverse);
140 } 143 }
141 144
@@ -145,32 +148,48 @@ public class DirectedCrossReferenceTranslator implements ModelStoreConfiguration
145 var targetType = info.targetType(); 148 var targetType = info.targetType();
146 var mayNewSource = createMayHelper(sourceType, info.sourceMultiplicity(), false); 149 var mayNewSource = createMayHelper(sourceType, info.sourceMultiplicity(), false);
147 var mayNewTarget = createMayHelper(targetType, info.targetMultiplicity(), true); 150 var mayNewTarget = createMayHelper(targetType, info.targetMultiplicity(), true);
148 var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class); 151 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(propagationBuilder -> propagationBuilder
149 propagationBuilder.rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> { 152 .rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> {
150 builder.clause( 153 builder.clause(
151 may(linkType.call(p1, p2)), 154 may(linkType.call(p1, p2)),
152 not(may(sourceType.call(p1))) 155 not(may(sourceType.call(p1)))
153 ); 156 );
154 builder.clause( 157 builder.clause(
155 may(linkType.call(p1, p2)), 158 may(linkType.call(p1, p2)),
156 not(may(targetType.call(p2))) 159 not(may(targetType.call(p2)))
157 ); 160 );
158 if (info.isConstrained()) { 161 if (info.isConstrained()) {
159 builder.clause( 162 builder.clause(
160 may(linkType.call(p1, p2)), 163 may(linkType.call(p1, p2)),
161 not(must(linkType.call(p1, p2))), 164 not(must(linkType.call(p1, p2))),
162 not(mayNewSource.call(p1)) 165 not(mayNewSource.call(p1))
163 ); 166 );
164 builder.clause( 167 builder.clause(
165 may(linkType.call(p1, p2)), 168 may(linkType.call(p1, p2)),
166 not(must(linkType.call(p1, p2))), 169 not(must(linkType.call(p1, p2))),
167 not(mayNewTarget.call(p2)) 170 not(mayNewTarget.call(p2))
168 ); 171 );
169 } 172 }
170 builder.action( 173 builder.action(
171 merge(linkType, TruthValue.FALSE, p1, p2) 174 remove(linkType, p1, p2)
172 ); 175 );
173 })); 176 })));
174 } 177 }
175 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 }
193 }
194 }
176} 195}
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 af0ddd2e..0a718033 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,12 +19,13 @@ 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;
22import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator; 23import tools.refinery.store.reasoning.translator.multiplicity.InvalidMultiplicityErrorTranslator;
23import tools.refinery.store.representation.Symbol; 24import tools.refinery.store.representation.Symbol;
24 25
25import static tools.refinery.logic.literal.Literals.not; 26import static tools.refinery.logic.literal.Literals.not;
26import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; 27import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add;
27import static tools.refinery.store.reasoning.actions.PartialActionLiterals.merge; 28import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove;
28import static tools.refinery.store.reasoning.literal.PartialLiterals.*; 29import static tools.refinery.store.reasoning.literal.PartialLiterals.*;
29import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; 30import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW;
30 31
@@ -72,6 +73,7 @@ public class UndirectedCrossReferenceTranslator implements ModelStoreConfigurati
72 } 73 }
73 storeBuilder.with(translator); 74 storeBuilder.with(translator);
74 storeBuilder.with(new InvalidMultiplicityErrorTranslator(type, linkType, false, info.multiplicity())); 75 storeBuilder.with(new InvalidMultiplicityErrorTranslator(type, linkType, false, info.multiplicity()));
76 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(this::configureLowerMultiplicityPropagator);
75 } 77 }
76 78
77 private void configureWithDefaultUnknown(PartialRelationTranslator translator) { 79 private void configureWithDefaultUnknown(PartialRelationTranslator translator) {
@@ -125,22 +127,33 @@ public class UndirectedCrossReferenceTranslator implements ModelStoreConfigurati
125 var name = linkType.name(); 127 var name = linkType.name();
126 var type = info.type(); 128 var type = info.type();
127 var mayNewSource = CrossReferenceUtils.createMayHelper(linkType, type, info.multiplicity(), false); 129 var mayNewSource = CrossReferenceUtils.createMayHelper(linkType, type, info.multiplicity(), false);
128 var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class); 130 storeBuilder.tryGetAdapter(PropagationBuilder.class).ifPresent(propagationBuilder -> propagationBuilder
129 propagationBuilder.rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> { 131 .rule(Rule.of(name + "#invalidLink", (builder, p1, p2) -> {
130 builder.clause( 132 builder.clause(
131 may(linkType.call(p1, p2)), 133 may(linkType.call(p1, p2)),
132 not(may(type.call(p1))) 134 not(may(type.call(p1)))
133 ); 135 );
134 if (info.isConstrained()) { 136 if (info.isConstrained()) {
135 builder.clause( 137 builder.clause(
136 may(linkType.call(p1, p2)), 138 may(linkType.call(p1, p2)),
137 not(must(linkType.call(p1, p2))), 139 not(must(linkType.call(p1, p2))),
138 not(mayNewSource.call(p1)) 140 not(mayNewSource.call(p1))
139 ); 141 );
142 }
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);
140 } 156 }
141 builder.action( 157 }
142 merge(linkType, TruthValue.FALSE, p1, p2)
143 );
144 }));
145 } 158 }
146} 159}