aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java141
1 files changed, 141 insertions, 0 deletions
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
new file mode 100644
index 00000000..ba208156
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiplicity/InvalidMultiplicityErrorTranslator.java
@@ -0,0 +1,141 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.reasoning.translator.multiplicity;
7
8import tools.refinery.store.dse.transition.objectives.Objectives;
9import tools.refinery.store.model.ModelStoreBuilder;
10import tools.refinery.store.model.ModelStoreConfiguration;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.query.term.Variable;
13import tools.refinery.store.query.term.int_.IntTerms;
14import tools.refinery.store.reasoning.ReasoningAdapter;
15import tools.refinery.store.reasoning.lifting.DnfLifter;
16import tools.refinery.store.reasoning.literal.*;
17import tools.refinery.store.reasoning.representation.PartialRelation;
18import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
19import tools.refinery.store.reasoning.translator.TranslationException;
20import tools.refinery.store.representation.cardinality.FiniteUpperCardinality;
21import tools.refinery.store.representation.cardinality.UpperCardinalities;
22import tools.refinery.store.representation.cardinality.UpperCardinality;
23
24import java.util.List;
25
26import static tools.refinery.store.query.literal.Literals.check;
27import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM;
28import static tools.refinery.store.query.term.int_.IntTerms.constant;
29import static tools.refinery.store.query.term.int_.IntTerms.greater;
30import static tools.refinery.store.query.term.int_.IntTerms.sub;
31import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.constant;
32import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.less;
33import static tools.refinery.store.reasoning.literal.PartialLiterals.candidateMust;
34import static tools.refinery.store.reasoning.literal.PartialLiterals.must;
35
36public class InvalidMultiplicityErrorTranslator implements ModelStoreConfiguration {
37 private final PartialRelation nodeType;
38 private final PartialRelation linkType;
39 private final boolean inverse;
40 private final Multiplicity multiplicity;
41
42 public InvalidMultiplicityErrorTranslator(PartialRelation nodeType, PartialRelation linkType,
43 boolean inverse, Multiplicity multiplicity) {
44 if (nodeType.arity() != 1) {
45 throw new TranslationException(linkType, "Node type must be of arity 1, got %s with arity %d instead"
46 .formatted(nodeType, nodeType.arity()));
47 }
48 if (linkType.arity() != 2) {
49 throw new TranslationException(linkType, "Link type must be of arity 2, got %s with arity %d instead"
50 .formatted(linkType, linkType.arity()));
51 }
52 this.nodeType = nodeType;
53 this.linkType = linkType;
54 this.inverse = inverse;
55 this.multiplicity = multiplicity;
56 }
57
58 @Override
59 public void apply(ModelStoreBuilder storeBuilder) {
60 if (!(multiplicity instanceof ConstrainedMultiplicity constrainedMultiplicity)) {
61 return;
62 }
63
64 var name = constrainedMultiplicity.errorSymbol().name();
65 var cardinalityInterval = constrainedMultiplicity.multiplicity();
66 var node = Variable.of("node");
67 var other = Variable.of("other");
68 List<Variable> arguments = inverse ? List.of(other, node) : List.of(node, other);
69 var mustBuilder = Query.builder(DnfLifter.decorateName(name, Modality.MUST, Concreteness.PARTIAL))
70 .parameter(node);
71 var candidateMayBuilder = Query.builder(DnfLifter.decorateName(name, Modality.MAY, Concreteness.PARTIAL))
72 .parameter(node);
73 var candidateMustBuilder = Query.builder(DnfLifter.decorateName(name, Modality.MUST, Concreteness.PARTIAL))
74 .parameter(node);
75 var missingOutput = Variable.of("missing", Integer.class);
76 var missingBuilder = Query.builder(name + "#missingMultiplicity").parameter(node).output(missingOutput);
77
78 int lowerBound = cardinalityInterval.lowerBound();
79 if (lowerBound > 0) {
80 var lowerBoundCardinality = UpperCardinalities.atMost(lowerBound);
81 mustBuilder.clause(UpperCardinality.class, existingContents -> List.of(
82 must(nodeType.call(node)),
83 new CountUpperBoundLiteral(existingContents, linkType, arguments),
84 check(less(existingContents, constant(lowerBoundCardinality)))
85 ));
86 candidateMayBuilder.clause(Integer.class, existingContents -> List.of(
87 candidateMust(nodeType.call(node)),
88 new CountCandidateLowerBoundLiteral(existingContents, linkType, arguments),
89 check(IntTerms.less(existingContents, constant(lowerBound)))
90 ));
91 candidateMustBuilder.clause(Integer.class, existingContents -> List.of(
92 candidateMust(nodeType.call(node)),
93 new CountCandidateUpperBoundLiteral(existingContents, linkType, arguments),
94 check(IntTerms.less(existingContents, constant(lowerBound)))
95 ));
96 missingBuilder.clause(Integer.class, existingContents -> List.of(
97 candidateMust(nodeType.call(node)),
98 new CountCandidateLowerBoundLiteral(existingContents, linkType, arguments),
99 missingOutput.assign(sub(constant(lowerBound), existingContents)),
100 check(greater(missingOutput, constant(0)))
101 ));
102 }
103
104 if (cardinalityInterval.upperBound() instanceof FiniteUpperCardinality finiteUpperCardinality) {
105 int upperBound = finiteUpperCardinality.finiteUpperBound();
106 mustBuilder.clause(Integer.class, existingContents -> List.of(
107 must(nodeType.call(node)),
108 new CountLowerBoundLiteral(existingContents, linkType, arguments),
109 check(greater(existingContents, constant(upperBound)))
110 ));
111 candidateMayBuilder.clause(Integer.class, existingContents -> List.of(
112 candidateMust(nodeType.call(node)),
113 new CountCandidateUpperBoundLiteral(existingContents, linkType, arguments),
114 check(greater(existingContents, constant(upperBound)))
115 ));
116 candidateMustBuilder.clause(Integer.class, existingContents -> List.of(
117 candidateMust(nodeType.call(node)),
118 new CountCandidateLowerBoundLiteral(existingContents, linkType, arguments),
119 check(greater(existingContents, constant(upperBound)))
120 ));
121 missingBuilder.clause(Integer.class, existingContents -> List.of(
122 candidateMust(nodeType.call(node)),
123 candidateMust(ReasoningAdapter.EXISTS_SYMBOL.call(node)),
124 new CountCandidateUpperBoundLiteral(existingContents, linkType, arguments),
125 missingOutput.assign(sub(existingContents, constant(upperBound))),
126 check(greater(missingOutput, constant(0)))
127 ));
128 }
129
130 var objective = Query.of(name + "#objective", Integer.class, (builder, output) -> builder.clause(
131 output.assign(missingBuilder.build().aggregate(INT_SUM, Variable.of()))
132 ));
133
134 storeBuilder.with(PartialRelationTranslator.of(constrainedMultiplicity.errorSymbol())
135 .mayNever()
136 .must(mustBuilder.build())
137 .candidateMay(candidateMayBuilder.build())
138 .candidateMust(candidateMustBuilder.build())
139 .objective(Objectives.value(objective)));
140 }
141}