aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2023-09-14 19:29:36 +0200
committerLibravatar GitHub <noreply@github.com>2023-09-14 19:29:36 +0200
commit98ed3b6db5f4e51961a161050cc31c66015116e8 (patch)
tree8bfd6d9bc8d6ed23b9eb0f889dd40b6c24fe8f92 /subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject
parentMerge pull request #38 from nagilooh/design-space-exploration (diff)
parentMerge remote-tracking branch 'upstream/main' into partial-interpretation (diff)
downloadrefinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.gz
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.tar.zst
refinery-98ed3b6db5f4e51961a161050cc31c66015116e8.zip
Merge pull request #39 from kris7t/partial-interpretation
Implement partial interpretation based model generation
Diffstat (limited to 'subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRefiner.java64
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRelationRewriter.java85
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/ExistsRefiner.java55
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/LowerCardinalityView.java22
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java135
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectStorageRefiner.java45
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java107
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java23
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/UpperCardinalityView.java23
9 files changed, 559 insertions, 0 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRefiner.java
new file mode 100644
index 00000000..d8db4ec4
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRefiner.java
@@ -0,0 +1,64 @@
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.multiobject;
7
8import tools.refinery.store.model.Interpretation;
9import tools.refinery.store.reasoning.ReasoningAdapter;
10import tools.refinery.store.reasoning.refinement.AbstractPartialInterpretationRefiner;
11import tools.refinery.store.reasoning.representation.PartialSymbol;
12import tools.refinery.store.representation.Symbol;
13import tools.refinery.store.representation.TruthValue;
14import tools.refinery.store.representation.cardinality.CardinalityInterval;
15import tools.refinery.store.representation.cardinality.CardinalityIntervals;
16import tools.refinery.store.tuple.Tuple;
17
18public class EqualsRefiner extends AbstractPartialInterpretationRefiner<TruthValue, Boolean> {
19 private final Interpretation<CardinalityInterval> countInterpretation;
20
21 private EqualsRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol,
22 Symbol<CardinalityInterval> countSymbol) {
23 super(adapter, partialSymbol);
24 countInterpretation = adapter.getModel().getInterpretation(countSymbol);
25 }
26
27 @Override
28 public boolean merge(Tuple key, TruthValue value) {
29 if (value == TruthValue.UNKNOWN) {
30 return true;
31 }
32 if (value == TruthValue.ERROR) {
33 return false;
34 }
35 int left = key.get(0);
36 int right = key.get(1);
37 boolean isDiagonal = left == right;
38 if (isDiagonal && value == TruthValue.FALSE) {
39 return false;
40 }
41 if (!isDiagonal) {
42 return !value.may();
43 }
44 if (value != TruthValue.TRUE) {
45 throw new IllegalArgumentException("Unknown TruthValue: " + value);
46 }
47 // {@code isDiagonal} is true, so this could be {@code left} or {@code right}.
48 var unaryKey = Tuple.of(left);
49 var currentCount = countInterpretation.get(unaryKey);
50 if (currentCount == null) {
51 return false;
52 }
53 var newCount = currentCount.meet(CardinalityIntervals.LONE);
54 if (newCount.isEmpty()) {
55 return false;
56 }
57 countInterpretation.put(unaryKey, newCount);
58 return true;
59 }
60
61 public static Factory<TruthValue, Boolean> of(Symbol<CardinalityInterval> countSymbol) {
62 return (adapter, partialSymbol) -> new EqualsRefiner(adapter, partialSymbol, countSymbol);
63 }
64}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRelationRewriter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRelationRewriter.java
new file mode 100644
index 00000000..61b9488c
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRelationRewriter.java
@@ -0,0 +1,85 @@
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.multiobject;
7
8import tools.refinery.store.query.dnf.Query;
9import tools.refinery.store.query.dnf.RelationalQuery;
10import tools.refinery.store.query.literal.AbstractCallLiteral;
11import tools.refinery.store.query.literal.CallLiteral;
12import tools.refinery.store.query.literal.Literal;
13import tools.refinery.store.query.term.Variable;
14import tools.refinery.store.query.view.AnySymbolView;
15import tools.refinery.store.reasoning.interpretation.QueryBasedRelationRewriter;
16import tools.refinery.store.reasoning.literal.Concreteness;
17import tools.refinery.store.reasoning.literal.Modality;
18import tools.refinery.store.representation.cardinality.UpperCardinalities;
19import tools.refinery.store.representation.cardinality.UpperCardinality;
20
21import java.util.List;
22import java.util.Set;
23
24import static tools.refinery.store.query.literal.Literals.check;
25import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.constant;
26import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.lessEq;
27
28class EqualsRelationRewriter extends QueryBasedRelationRewriter {
29 private EqualsRelationRewriter(RelationalQuery may, RelationalQuery must) {
30 super(may, must, may, may);
31 }
32
33 @Override
34 public List<Literal> rewriteLiteral(Set<Variable> positiveVariables, AbstractCallLiteral literal,
35 Modality modality, Concreteness concreteness) {
36 if (!(literal instanceof CallLiteral callLiteral)) {
37 return super.rewriteLiteral(positiveVariables, literal, modality, concreteness);
38 }
39 var left = callLiteral.getArguments().get(0).asNodeVariable();
40 var right = callLiteral.getArguments().get(1).asNodeVariable();
41 boolean useMay = modality == Modality.MAY || concreteness == Concreteness.CANDIDATE;
42 return switch (callLiteral.getPolarity()) {
43 case POSITIVE, TRANSITIVE -> {
44 if (useMay) {
45 if (positiveVariables.contains(left) || positiveVariables.contains(right)) {
46 // No need to enumerate arguments if at least one is already bound, since they will be unified.
47 yield List.of(left.isEquivalent(right));
48 } else {
49 yield List.of(
50 left.isEquivalent(right),
51 getMay().call(left, right)
52 );
53 }
54 } else {
55 yield List.of(
56 left.isEquivalent(right),
57 getMust().call(left, right)
58 );
59 }
60 }
61 case NEGATIVE -> {
62 if (useMay) {
63 yield List.of(left.notEquivalent(right));
64 } else {
65 yield super.rewriteLiteral(positiveVariables, literal, modality, concreteness);
66 }
67 }
68 };
69 }
70
71 public static EqualsRelationRewriter of(AnySymbolView upperCardinalityView) {
72 var may = Query.of("equals#may", (builder, p1, p2) -> builder
73 .clause(
74 p1.isEquivalent(p2),
75 upperCardinalityView.call(p1, Variable.of(UpperCardinality.class))
76 ));
77 var must = Query.of("equals#must", (builder, p1, p2) -> builder
78 .clause(UpperCardinality.class, upper -> List.of(
79 p1.isEquivalent(p2),
80 upperCardinalityView.call(p1, upper),
81 check(lessEq(upper, constant(UpperCardinalities.ONE)))
82 )));
83 return new EqualsRelationRewriter(may, must);
84 }
85}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/ExistsRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/ExistsRefiner.java
new file mode 100644
index 00000000..f134fe92
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/ExistsRefiner.java
@@ -0,0 +1,55 @@
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.multiobject;
7
8import tools.refinery.store.model.Interpretation;
9import tools.refinery.store.reasoning.ReasoningAdapter;
10import tools.refinery.store.reasoning.refinement.AbstractPartialInterpretationRefiner;
11import tools.refinery.store.reasoning.representation.PartialSymbol;
12import tools.refinery.store.representation.Symbol;
13import tools.refinery.store.representation.TruthValue;
14import tools.refinery.store.representation.cardinality.CardinalityInterval;
15import tools.refinery.store.representation.cardinality.CardinalityIntervals;
16import tools.refinery.store.tuple.Tuple;
17
18public class ExistsRefiner extends AbstractPartialInterpretationRefiner<TruthValue, Boolean> {
19 private final Interpretation<CardinalityInterval> countInterpretation;
20
21 private ExistsRefiner(ReasoningAdapter adapter, PartialSymbol<TruthValue, Boolean> partialSymbol,
22 Symbol<CardinalityInterval> countSymbol) {
23 super(adapter, partialSymbol);
24 countInterpretation = adapter.getModel().getInterpretation(countSymbol);
25 }
26
27 @Override
28 public boolean merge(Tuple key, TruthValue value) {
29 var currentCount = countInterpretation.get(key);
30 if (currentCount == null) {
31 return false;
32 }
33 CardinalityInterval newCount;
34 switch (value) {
35 case UNKNOWN -> {
36 return true;
37 }
38 case TRUE -> newCount = currentCount.meet(CardinalityIntervals.SOME);
39 case FALSE -> newCount = currentCount.meet(CardinalityIntervals.NONE);
40 case ERROR -> {
41 return false;
42 }
43 default -> throw new IllegalArgumentException("Unknown TruthValue: " + value);
44 }
45 if (newCount.isEmpty()) {
46 return false;
47 }
48 countInterpretation.put(key, newCount);
49 return true;
50 }
51
52 public static Factory<TruthValue, Boolean> of(Symbol<CardinalityInterval> countSymbol) {
53 return (adapter, partialSymbol) -> new ExistsRefiner(adapter, partialSymbol, countSymbol);
54 }
55}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/LowerCardinalityView.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/LowerCardinalityView.java
new file mode 100644
index 00000000..9873888c
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/LowerCardinalityView.java
@@ -0,0 +1,22 @@
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.multiobject;
7
8import tools.refinery.store.query.term.Parameter;
9import tools.refinery.store.query.view.AbstractFunctionView;
10import tools.refinery.store.representation.Symbol;
11import tools.refinery.store.representation.cardinality.CardinalityInterval;
12
13class LowerCardinalityView extends AbstractFunctionView<CardinalityInterval> {
14 public LowerCardinalityView(Symbol<CardinalityInterval> symbol) {
15 super(symbol, "lower", new Parameter(Integer.class));
16 }
17
18 @Override
19 protected Object forwardMapValue(CardinalityInterval value) {
20 return value.lowerBound();
21 }
22}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java
new file mode 100644
index 00000000..89918155
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java
@@ -0,0 +1,135 @@
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.multiobject;
7
8import org.jetbrains.annotations.NotNull;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.reasoning.ReasoningAdapter;
11import tools.refinery.store.reasoning.refinement.PartialModelInitializer;
12import tools.refinery.store.reasoning.seed.ModelSeed;
13import tools.refinery.store.reasoning.translator.TranslationException;
14import tools.refinery.store.representation.Symbol;
15import tools.refinery.store.representation.TruthValue;
16import tools.refinery.store.representation.cardinality.CardinalityInterval;
17import tools.refinery.store.representation.cardinality.CardinalityIntervals;
18import tools.refinery.store.tuple.Tuple;
19
20import java.util.Arrays;
21import java.util.HashMap;
22import java.util.function.Function;
23
24class MultiObjectInitializer implements PartialModelInitializer {
25 private final Symbol<CardinalityInterval> countSymbol;
26
27 public MultiObjectInitializer(Symbol<CardinalityInterval> countSymbol) {
28 this.countSymbol = countSymbol;
29 }
30
31 @Override
32 public void initialize(Model model, ModelSeed modelSeed) {
33 var intervals = initializeIntervals(model, modelSeed);
34 initializeExists(intervals, model, modelSeed);
35 initializeEquals(intervals, model, modelSeed);
36 var countInterpretation = model.getInterpretation(countSymbol);
37 var uniqueTable = new HashMap<CardinalityInterval, CardinalityInterval>();
38 for (int i = 0; i < intervals.length; i++) {
39 var interval = intervals[i];
40 if (interval.isEmpty()) {
41 throw new TranslationException(ReasoningAdapter.EXISTS_SYMBOL,
42 "Inconsistent existence or equality for node " + i);
43 }
44 var uniqueInterval = uniqueTable.computeIfAbsent(intervals[i], Function.identity());
45 countInterpretation.put(Tuple.of(i), uniqueInterval);
46 }
47 }
48
49 @NotNull
50 private CardinalityInterval[] initializeIntervals(Model model, ModelSeed modelSeed) {
51 var intervals = new CardinalityInterval[modelSeed.getNodeCount()];
52 if (modelSeed.containsSeed(MultiObjectTranslator.COUNT_SYMBOL)) {
53 Arrays.fill(intervals, CardinalityIntervals.ONE);
54 var cursor = modelSeed.getCursor(MultiObjectTranslator.COUNT_SYMBOL, CardinalityIntervals.ONE);
55 while (cursor.move()) {
56 model.checkCancelled();
57 int i = cursor.getKey().get(0);
58 checkNodeId(intervals, i);
59 intervals[i] = cursor.getValue();
60 }
61 } else {
62 Arrays.fill(intervals, CardinalityIntervals.SET);
63 if (!modelSeed.containsSeed(ReasoningAdapter.EXISTS_SYMBOL) ||
64 !modelSeed.containsSeed(ReasoningAdapter.EQUALS_SYMBOL)) {
65 throw new TranslationException(MultiObjectTranslator.COUNT_SYMBOL,
66 "Seed for %s and %s is required if there is no seed for %s".formatted(
67 ReasoningAdapter.EXISTS_SYMBOL, ReasoningAdapter.EQUALS_SYMBOL,
68 MultiObjectTranslator.COUNT_SYMBOL));
69 }
70 }
71 return intervals;
72 }
73
74 private void initializeExists(CardinalityInterval[] intervals, Model model, ModelSeed modelSeed) {
75 if (!modelSeed.containsSeed(ReasoningAdapter.EXISTS_SYMBOL)) {
76 return;
77 }
78 var cursor = modelSeed.getCursor(ReasoningAdapter.EXISTS_SYMBOL, TruthValue.UNKNOWN);
79 while (cursor.move()) {
80 model.checkCancelled();
81 int i = cursor.getKey().get(0);
82 checkNodeId(intervals, i);
83 switch (cursor.getValue()) {
84 case TRUE -> intervals[i] = intervals[i].meet(CardinalityIntervals.SOME);
85 case FALSE -> intervals[i] = intervals[i].meet(CardinalityIntervals.NONE);
86 case ERROR -> throw new TranslationException(ReasoningAdapter.EXISTS_SYMBOL,
87 "Inconsistent existence for node " + i);
88 default -> throw new TranslationException(ReasoningAdapter.EXISTS_SYMBOL,
89 "Invalid existence truth value %s for node %d".formatted(cursor.getValue(), i));
90 }
91 }
92 }
93
94 private void initializeEquals(CardinalityInterval[] intervals, Model model, ModelSeed modelSeed) {
95 if (!modelSeed.containsSeed(ReasoningAdapter.EQUALS_SYMBOL)) {
96 return;
97 }
98 var seed = modelSeed.getSeed(ReasoningAdapter.EQUALS_SYMBOL);
99 var cursor = seed.getCursor(TruthValue.FALSE, modelSeed.getNodeCount());
100 while (cursor.move()) {
101 model.checkCancelled();
102 var key = cursor.getKey();
103 int i = key.get(0);
104 int otherIndex = key.get(1);
105 if (i != otherIndex) {
106 throw new TranslationException(ReasoningAdapter.EQUALS_SYMBOL,
107 "Off-diagonal equivalence (%d, %d) is not permitted".formatted(i, otherIndex));
108 }
109 checkNodeId(intervals, i);
110 switch (cursor.getValue()) {
111 case TRUE -> intervals[i] = intervals[i].meet(CardinalityIntervals.LONE);
112 case UNKNOWN -> {
113 // Nothing do to, {@code intervals} is initialized with unknown equality.
114 }
115 case ERROR -> throw new TranslationException(ReasoningAdapter.EQUALS_SYMBOL,
116 "Inconsistent equality for node " + i);
117 default -> throw new TranslationException(ReasoningAdapter.EQUALS_SYMBOL,
118 "Invalid equality truth value %s for node %d".formatted(cursor.getValue(), i));
119 }
120 }
121 for (int i = 0; i < intervals.length; i++) {
122 model.checkCancelled();
123 if (seed.get(Tuple.of(i, i)) == TruthValue.FALSE) {
124 throw new TranslationException(ReasoningAdapter.EQUALS_SYMBOL, "Inconsistent equality for node " + i);
125 }
126 }
127 }
128
129 private void checkNodeId(CardinalityInterval[] intervals, int nodeId) {
130 if (nodeId < 0 || nodeId >= intervals.length) {
131 throw new IllegalArgumentException("Expected node id %d to be lower than model size %d"
132 .formatted(nodeId, intervals.length));
133 }
134 }
135}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectStorageRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectStorageRefiner.java
new file mode 100644
index 00000000..e48934d8
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectStorageRefiner.java
@@ -0,0 +1,45 @@
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.multiobject;
7
8import tools.refinery.store.model.Interpretation;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.reasoning.refinement.StorageRefiner;
11import tools.refinery.store.representation.Symbol;
12import tools.refinery.store.representation.cardinality.CardinalityInterval;
13import tools.refinery.store.representation.cardinality.CardinalityIntervals;
14import tools.refinery.store.tuple.Tuple;
15
16class MultiObjectStorageRefiner implements StorageRefiner {
17 private final Interpretation<CardinalityInterval> countInterpretation;
18
19 public MultiObjectStorageRefiner(Symbol<CardinalityInterval> countSymbol, Model model) {
20 countInterpretation = model.getInterpretation(countSymbol);
21 }
22
23 @Override
24 public boolean split(int parentNode, int childNode) {
25 var parentKey = Tuple.of(parentNode);
26 var parentCount = countInterpretation.get(parentKey);
27 if (parentCount == null) {
28 return false;
29 }
30 var newParentCount = parentCount.take(1);
31 if (newParentCount.isEmpty()) {
32 return false;
33 }
34 var childKey = Tuple.of(childNode);
35 countInterpretation.put(parentKey, newParentCount);
36 countInterpretation.put(childKey, CardinalityIntervals.ONE);
37 return true;
38 }
39
40 @Override
41 public boolean cleanup(int nodeToDelete) {
42 var previousCount = countInterpretation.put(Tuple.of(nodeToDelete), null);
43 return previousCount.lowerBound() == 0;
44 }
45}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java
new file mode 100644
index 00000000..97fda9d5
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java
@@ -0,0 +1,107 @@
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.multiobject;
7
8import tools.refinery.store.dse.propagation.PropagationBuilder;
9import tools.refinery.store.dse.transition.Rule;
10import tools.refinery.store.dse.transition.objectives.Criteria;
11import tools.refinery.store.dse.transition.objectives.Objectives;
12import tools.refinery.store.model.ModelStoreBuilder;
13import tools.refinery.store.model.ModelStoreConfiguration;
14import tools.refinery.store.query.dnf.Query;
15import tools.refinery.store.query.literal.Literals;
16import tools.refinery.store.query.term.Variable;
17import tools.refinery.store.query.term.int_.IntTerms;
18import tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms;
19import tools.refinery.store.query.view.AnySymbolView;
20import tools.refinery.store.reasoning.ReasoningAdapter;
21import tools.refinery.store.reasoning.ReasoningBuilder;
22import tools.refinery.store.reasoning.actions.PartialActionLiterals;
23import tools.refinery.store.reasoning.representation.PartialFunction;
24import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
25import tools.refinery.store.reasoning.translator.RoundingMode;
26import tools.refinery.store.representation.Symbol;
27import tools.refinery.store.representation.cardinality.CardinalityDomain;
28import tools.refinery.store.representation.cardinality.CardinalityInterval;
29import tools.refinery.store.representation.cardinality.UpperCardinalities;
30import tools.refinery.store.representation.cardinality.UpperCardinality;
31
32import java.util.List;
33
34import static tools.refinery.store.query.literal.Literals.check;
35import static tools.refinery.store.query.term.int_.IntTerms.*;
36
37public class MultiObjectTranslator implements ModelStoreConfiguration {
38 public static final Symbol<CardinalityInterval> COUNT_STORAGE = Symbol.of("COUNT", 1, CardinalityInterval.class,
39 null);
40 public static final AnySymbolView LOWER_CARDINALITY_VIEW = new LowerCardinalityView(COUNT_STORAGE);
41 public static final AnySymbolView UPPER_CARDINALITY_VIEW = new UpperCardinalityView(COUNT_STORAGE);
42 public static final AnySymbolView MULTI_VIEW = new MultiView(COUNT_STORAGE);
43 public static final PartialFunction<CardinalityInterval, Integer> COUNT_SYMBOL = new PartialFunction<>("COUNT", 1,
44 CardinalityDomain.INSTANCE);
45
46 @Override
47 public void apply(ModelStoreBuilder storeBuilder) {
48 storeBuilder.symbol(COUNT_STORAGE);
49
50 var aboveLowerBound = Query.of("count#aboveLowerBound", Integer.class, (builder, node, output) -> builder
51 .clause(
52 MULTI_VIEW.call(node),
53 LOWER_CARDINALITY_VIEW.call(node, output),
54 check(greater(output, IntTerms.constant(0)))
55 ));
56 var missingCardinality = Query.of("count#missing", Integer.class, (builder, output) -> builder
57 .clause(
58 output.assign(aboveLowerBound.aggregate(INT_SUM, Variable.of()))
59 ));
60
61 storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EXISTS_SYMBOL)
62 .may(Query.of("exists#may", (builder, p1) -> builder
63 .clause(UpperCardinality.class, upper -> List.of(
64 UPPER_CARDINALITY_VIEW.call(p1, upper),
65 check(UpperCardinalityTerms.greaterEq(upper,
66 UpperCardinalityTerms.constant(UpperCardinalities.ONE)))
67 ))))
68 .must(Query.of("exists#must", (builder, p1) -> builder
69 .clause(Integer.class, lower -> List.of(
70 LOWER_CARDINALITY_VIEW.call(p1, lower),
71 check(greaterEq(lower, constant(1)))
72 ))))
73 .candidate(Query.of("exists#candidate", (builder, p1) -> builder
74 .clause(
75 LOWER_CARDINALITY_VIEW.call(p1, Variable.of(Integer.class)),
76 Literals.not(MULTI_VIEW.call(p1))
77 )))
78 .roundingMode(RoundingMode.PREFER_FALSE)
79 .refiner(ExistsRefiner.of(COUNT_STORAGE))
80 .exclude(null)
81 .accept(Criteria.whenNoMatch(aboveLowerBound))
82 .objective(Objectives.value(missingCardinality)));
83
84 storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EQUALS_SYMBOL)
85 .rewriter(EqualsRelationRewriter.of(UPPER_CARDINALITY_VIEW))
86 .refiner(EqualsRefiner.of(COUNT_STORAGE))
87 .exclude(null)
88 .accept(null)
89 .objective(null));
90
91 var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class);
92 reasoningBuilder.initializer(new MultiObjectInitializer(COUNT_STORAGE));
93 reasoningBuilder.storageRefiner(COUNT_STORAGE, MultiObjectStorageRefiner::new);
94
95 storeBuilder.tryGetAdapter(PropagationBuilder.class)
96 .ifPresent(propagationBuilder -> propagationBuilder.rule(
97 Rule.of("exists#cleanup", (builder, node) -> builder
98 .clause(UpperCardinality.class, upper -> List.of(
99 UPPER_CARDINALITY_VIEW.call(node, upper),
100 check(UpperCardinalityTerms.less(upper,
101 UpperCardinalityTerms.constant(UpperCardinalities.ONE)))
102 ))
103 .action(
104 PartialActionLiterals.cleanup(node)
105 ))));
106 }
107}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java
new file mode 100644
index 00000000..498bcd83
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiView.java
@@ -0,0 +1,23 @@
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.multiobject;
7
8import tools.refinery.store.query.view.TuplePreservingView;
9import tools.refinery.store.representation.Symbol;
10import tools.refinery.store.representation.cardinality.CardinalityInterval;
11import tools.refinery.store.representation.cardinality.CardinalityIntervals;
12import tools.refinery.store.tuple.Tuple;
13
14class MultiView extends TuplePreservingView<CardinalityInterval> {
15 protected MultiView(Symbol<CardinalityInterval> symbol) {
16 super(symbol, "multi");
17 }
18
19 @Override
20 protected boolean doFilter(Tuple key, CardinalityInterval value) {
21 return !CardinalityIntervals.ONE.equals(value);
22 }
23}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/UpperCardinalityView.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/UpperCardinalityView.java
new file mode 100644
index 00000000..6be6ae1b
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/UpperCardinalityView.java
@@ -0,0 +1,23 @@
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.multiobject;
7
8import tools.refinery.store.query.term.Parameter;
9import tools.refinery.store.query.view.AbstractFunctionView;
10import tools.refinery.store.representation.Symbol;
11import tools.refinery.store.representation.cardinality.CardinalityInterval;
12import tools.refinery.store.representation.cardinality.UpperCardinality;
13
14class UpperCardinalityView extends AbstractFunctionView<CardinalityInterval> {
15 public UpperCardinalityView(Symbol<CardinalityInterval> symbol) {
16 super(symbol, "upper", new Parameter(UpperCardinality.class));
17 }
18
19 @Override
20 protected Object forwardMapValue(CardinalityInterval value) {
21 return value.upperBound();
22 }
23}