aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-07-25 16:06:36 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-07-25 16:06:36 +0200
commit6a25ba145844c79d3507f8eabdbed854be2b8097 (patch)
tree0ea9d4c7a9b5b94a0d4341eaa25eeb7e4d3f4f56 /subprojects/store-reasoning/src
parentfeat: custom connected component RETE node (diff)
downloadrefinery-6a25ba145844c79d3507f8eabdbed854be2b8097.tar.gz
refinery-6a25ba145844c79d3507f8eabdbed854be2b8097.tar.zst
refinery-6a25ba145844c79d3507f8eabdbed854be2b8097.zip
feat: concrete count in partial models
Diffstat (limited to 'subprojects/store-reasoning/src')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java93
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ClauseLifter.java4
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ConcreteCountLiteral.java47
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountLowerBoundLiteral.java48
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountUpperBoundLiteral.java50
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java4
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/EqualsRelationRewriter.java3
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectInitializer.java33
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/multiobject/MultiObjectTranslator.java27
-rw-r--r--subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/ConcreteCountTest.java324
10 files changed, 613 insertions, 20 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java
index bc379c64..c560162e 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/PartialClauseRewriter.java
@@ -5,17 +5,25 @@
5 */ 5 */
6package tools.refinery.store.reasoning.internal; 6package tools.refinery.store.reasoning.internal;
7 7
8import tools.refinery.store.query.Constraint;
8import tools.refinery.store.query.dnf.Dnf; 9import tools.refinery.store.query.dnf.Dnf;
9import tools.refinery.store.query.dnf.DnfClause; 10import tools.refinery.store.query.dnf.DnfClause;
10import tools.refinery.store.query.literal.AbstractCallLiteral; 11import tools.refinery.store.query.literal.AbstractCallLiteral;
12import tools.refinery.store.query.literal.CallPolarity;
11import tools.refinery.store.query.literal.Literal; 13import tools.refinery.store.query.literal.Literal;
14import tools.refinery.store.query.term.Aggregator;
15import tools.refinery.store.query.term.ConstantTerm;
16import tools.refinery.store.query.term.Term;
12import tools.refinery.store.query.term.Variable; 17import tools.refinery.store.query.term.Variable;
13import tools.refinery.store.reasoning.literal.Concreteness; 18import tools.refinery.store.query.term.int_.IntTerms;
14import tools.refinery.store.reasoning.literal.ModalConstraint; 19import tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms;
15import tools.refinery.store.reasoning.literal.Modality; 20import tools.refinery.store.reasoning.literal.*;
16import tools.refinery.store.reasoning.representation.PartialRelation; 21import tools.refinery.store.reasoning.representation.PartialRelation;
22import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator;
23import tools.refinery.store.representation.cardinality.UpperCardinalities;
17 24
18import java.util.*; 25import java.util.*;
26import java.util.function.BinaryOperator;
19 27
20class PartialClauseRewriter { 28class PartialClauseRewriter {
21 private final PartialQueryRewriter rewriter; 29 private final PartialQueryRewriter rewriter;
@@ -42,6 +50,14 @@ class PartialClauseRewriter {
42 markAsDone(literal); 50 markAsDone(literal);
43 return; 51 return;
44 } 52 }
53 if (callLiteral instanceof CountLowerBoundLiteral countLowerBoundLiteral) {
54 rewriteCountLowerBound(countLowerBoundLiteral);
55 return;
56 }
57 if (callLiteral instanceof CountUpperBoundLiteral countUpperBoundLiteral) {
58 rewriteCountUpperBound(countUpperBoundLiteral);
59 return;
60 }
45 var target = callLiteral.getTarget(); 61 var target = callLiteral.getTarget();
46 if (target instanceof Dnf dnf) { 62 if (target instanceof Dnf dnf) {
47 rewriteRecursively(callLiteral, dnf); 63 rewriteRecursively(callLiteral, dnf);
@@ -61,6 +77,75 @@ class PartialClauseRewriter {
61 } 77 }
62 } 78 }
63 79
80 private void rewriteCountLowerBound(CountLowerBoundLiteral literal) {
81 rewriteCount(literal, "lower", Modality.MUST, MultiObjectTranslator.LOWER_CARDINALITY_VIEW, 1, IntTerms::mul,
82 IntTerms.INT_SUM);
83 }
84
85 private void rewriteCountUpperBound(CountUpperBoundLiteral literal) {
86 rewriteCount(literal, "upper", Modality.MAY, MultiObjectTranslator.UPPER_CARDINALITY_VIEW,
87 UpperCardinalities.ONE, UpperCardinalityTerms::mul, UpperCardinalityTerms.UPPER_CARDINALITY_SUM);
88 }
89
90 private <T> void rewriteCount(ConcreteCountLiteral<T> literal, String name, Modality modality, Constraint view,
91 T one, BinaryOperator<Term<T>> mul, Aggregator<T, T> sum) {
92 var type = literal.getResultType();
93 var target = literal.getTarget();
94 var concreteness = literal.getConcreteness();
95 int arity = target.arity();
96 var parameters = target.getParameters();
97 var literalArguments = literal.getArguments();
98 var privateVariables = literal.getPrivateVariables(positiveVariables);
99
100 var builder = Dnf.builder("%s#%s#%s".formatted(target.name(), concreteness, name));
101 var rewrittenArguments = new ArrayList<Variable>(parameters.size());
102 var variablesToCount = new ArrayList<Variable>();
103 var helperArguments = new ArrayList<Variable>();
104 var literalToRewrittenArgumentMap = new HashMap<Variable, Variable>();
105 for (int i = 0; i < arity; i++) {
106 var literalArgument = literalArguments.get(i);
107 var parameter = parameters.get(i);
108 var rewrittenArgument = literalToRewrittenArgumentMap.computeIfAbsent(literalArgument, key -> {
109 helperArguments.add(key);
110 var newArgument = builder.parameter(parameter);
111 if (privateVariables.contains(key)) {
112 variablesToCount.add(newArgument);
113 }
114 return newArgument;
115 });
116 rewrittenArguments.add(rewrittenArgument);
117 }
118 var outputVariable = builder.parameter(type);
119
120 var literals = new ArrayList<Literal>();
121 literals.add(new ModalConstraint(modality, literal.getConcreteness(), target)
122 .call(CallPolarity.POSITIVE, rewrittenArguments));
123 switch (variablesToCount.size()) {
124 case 0 -> literals.add(outputVariable.assign(new ConstantTerm<>(type, one)));
125 case 1 -> literals.add(view.call(variablesToCount.get(0),
126 outputVariable));
127 default -> {
128 var firstCount = Variable.of(type);
129 literals.add(view.call(variablesToCount.get(0), firstCount));
130 int length = variablesToCount.size();
131 Term<T> accumulator = firstCount;
132 for (int i = 1; i < length; i++) {
133 var countVariable = Variable.of(type);
134 literals.add(view.call(variablesToCount.get(i), countVariable));
135 accumulator = mul.apply(accumulator, countVariable);
136 }
137 literals.add(outputVariable.assign(accumulator));
138 }
139 }
140 builder.clause(literals);
141
142 var helperQuery = builder.build();
143 var aggregationVariable = Variable.of(type);
144 helperArguments.add(aggregationVariable);
145 workList.addFirst(literal.getResultVariable().assign(helperQuery.aggregateBy(aggregationVariable, sum,
146 helperArguments)));
147 }
148
64 private void markAsDone(Literal literal) { 149 private void markAsDone(Literal literal) {
65 completedLiterals.add(literal); 150 completedLiterals.add(literal);
66 positiveVariables.addAll(literal.getOutputVariables()); 151 positiveVariables.addAll(literal.getOutputVariables());
@@ -75,7 +160,7 @@ class PartialClauseRewriter {
75 private void rewriteRecursively(AbstractCallLiteral callLiteral, Dnf dnf) { 160 private void rewriteRecursively(AbstractCallLiteral callLiteral, Dnf dnf) {
76 var rewrittenDnf = rewriter.rewrite(dnf); 161 var rewrittenDnf = rewriter.rewrite(dnf);
77 var rewrittenLiteral = callLiteral.withTarget(rewrittenDnf); 162 var rewrittenLiteral = callLiteral.withTarget(rewrittenDnf);
78 completedLiterals.add(rewrittenLiteral); 163 markAsDone(rewrittenLiteral);
79 } 164 }
80 165
81 private void rewrite(AbstractCallLiteral callLiteral, Modality modality, Concreteness concreteness, 166 private void rewrite(AbstractCallLiteral callLiteral, Modality modality, Concreteness concreteness,
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ClauseLifter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ClauseLifter.java
index 89e948dc..17916c02 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ClauseLifter.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ClauseLifter.java
@@ -70,9 +70,9 @@ class ClauseLifter {
70 literal instanceof AssignLiteral<?> || 70 literal instanceof AssignLiteral<?> ||
71 literal instanceof CheckLiteral) { 71 literal instanceof CheckLiteral) {
72 return literal; 72 return literal;
73 } else if (literal instanceof CountLiteral) { 73 } else if (literal instanceof AbstractCountLiteral<?>) {
74 throw new IllegalArgumentException("Count literal %s cannot be lifted".formatted(literal)); 74 throw new IllegalArgumentException("Count literal %s cannot be lifted".formatted(literal));
75 } else if (literal instanceof AggregationLiteral<?,?>) { 75 } else if (literal instanceof AggregationLiteral<?, ?>) {
76 throw new IllegalArgumentException("Aggregation literal %s cannot be lifted".formatted(literal)); 76 throw new IllegalArgumentException("Aggregation literal %s cannot be lifted".formatted(literal));
77 } else if (literal instanceof RepresentativeElectionLiteral) { 77 } else if (literal instanceof RepresentativeElectionLiteral) {
78 throw new IllegalArgumentException("SCC literal %s cannot be lifted".formatted(literal)); 78 throw new IllegalArgumentException("SCC literal %s cannot be lifted".formatted(literal));
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ConcreteCountLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ConcreteCountLiteral.java
new file mode 100644
index 00000000..91b30964
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ConcreteCountLiteral.java
@@ -0,0 +1,47 @@
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.literal;
7
8import tools.refinery.store.query.Constraint;
9import tools.refinery.store.query.equality.LiteralEqualityHelper;
10import tools.refinery.store.query.equality.LiteralHashCodeHelper;
11import tools.refinery.store.query.literal.AbstractCountLiteral;
12import tools.refinery.store.query.literal.Literal;
13import tools.refinery.store.query.term.DataVariable;
14import tools.refinery.store.query.term.Variable;
15
16import java.util.List;
17import java.util.Objects;
18
19// {@link Object#equals(Object)} is implemented by {@link AbstractLiteral}.
20@SuppressWarnings("squid:S2160")
21public abstract class ConcreteCountLiteral<T> extends AbstractCountLiteral<T> {
22 private final Concreteness concreteness;
23
24 protected ConcreteCountLiteral(Class<T> resultType, DataVariable<T> resultVariable, Concreteness concreteness,
25 Constraint target, List<Variable> arguments) {
26 super(resultType, resultVariable, target, arguments);
27 this.concreteness = concreteness;
28 }
29
30 public Concreteness getConcreteness() {
31 return concreteness;
32 }
33
34 @Override
35 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
36 if (!super.equalsWithSubstitution(helper, other)) {
37 return false;
38 }
39 var otherCountLiteral = (ConcreteCountLiteral<?>) other;
40 return Objects.equals(concreteness, otherCountLiteral.concreteness);
41 }
42
43 @Override
44 public int hashCodeWithSubstitution(LiteralHashCodeHelper helper) {
45 return Objects.hash(super.hashCodeWithSubstitution(helper), concreteness);
46 }
47}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountLowerBoundLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountLowerBoundLiteral.java
new file mode 100644
index 00000000..261088fc
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountLowerBoundLiteral.java
@@ -0,0 +1,48 @@
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.literal;
7
8import tools.refinery.store.query.Constraint;
9import tools.refinery.store.query.literal.AbstractCallLiteral;
10import tools.refinery.store.query.literal.Literal;
11import tools.refinery.store.query.substitution.Substitution;
12import tools.refinery.store.query.term.DataVariable;
13import tools.refinery.store.query.term.Variable;
14
15import java.util.List;
16
17public class CountLowerBoundLiteral extends ConcreteCountLiteral<Integer> {
18 public CountLowerBoundLiteral(DataVariable<Integer> resultVariable, Concreteness concreteness, Constraint target,
19 List<Variable> arguments) {
20 super(Integer.class, resultVariable, concreteness, target, arguments);
21 }
22
23 @Override
24 protected Integer zero() {
25 return 0;
26 }
27
28 @Override
29 protected Integer one() {
30 return 1;
31 }
32
33 @Override
34 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
35 return new CountLowerBoundLiteral(substitution.getTypeSafeSubstitute(getResultVariable()), getConcreteness(),
36 getTarget(), substitutedArguments);
37 }
38
39 @Override
40 protected AbstractCallLiteral internalWithTarget(Constraint newTarget) {
41 return new CountLowerBoundLiteral(getResultVariable(), getConcreteness(), newTarget, getArguments());
42 }
43
44 @Override
45 protected String operatorName() {
46 return "@LowerBound(\"%s\") count".formatted(getConcreteness());
47 }
48}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountUpperBoundLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountUpperBoundLiteral.java
new file mode 100644
index 00000000..960ded69
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/CountUpperBoundLiteral.java
@@ -0,0 +1,50 @@
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.literal;
7
8import tools.refinery.store.query.Constraint;
9import tools.refinery.store.query.literal.AbstractCallLiteral;
10import tools.refinery.store.query.literal.Literal;
11import tools.refinery.store.query.substitution.Substitution;
12import tools.refinery.store.query.term.DataVariable;
13import tools.refinery.store.query.term.Variable;
14import tools.refinery.store.representation.cardinality.UpperCardinalities;
15import tools.refinery.store.representation.cardinality.UpperCardinality;
16
17import java.util.List;
18
19public class CountUpperBoundLiteral extends ConcreteCountLiteral<UpperCardinality> {
20 public CountUpperBoundLiteral(DataVariable<UpperCardinality> resultVariable, Concreteness concreteness,
21 Constraint target, List<Variable> arguments) {
22 super(UpperCardinality.class, resultVariable, concreteness, target, arguments);
23 }
24
25 @Override
26 protected UpperCardinality zero() {
27 return UpperCardinalities.ZERO;
28 }
29
30 @Override
31 protected UpperCardinality one() {
32 return UpperCardinalities.UNBOUNDED;
33 }
34
35 @Override
36 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
37 return new CountUpperBoundLiteral(substitution.getTypeSafeSubstitute(getResultVariable()), getConcreteness(),
38 getTarget(), substitutedArguments);
39 }
40
41 @Override
42 protected AbstractCallLiteral internalWithTarget(Constraint newTarget) {
43 return new CountUpperBoundLiteral(getResultVariable(), getConcreteness(), newTarget, getArguments());
44 }
45
46 @Override
47 protected String operatorName() {
48 return "@UpperBound(\"%s\") count".formatted(getConcreteness());
49 }
50}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java
index 3641730d..28e6258e 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java
@@ -39,6 +39,10 @@ public class ModelSeed {
39 return typedSeed; 39 return typedSeed;
40 } 40 }
41 41
42 public boolean containsSeed(AnyPartialSymbol symbol) {
43 return seeds.containsKey(symbol);
44 }
45
42 public <A> Cursor<Tuple, A> getCursor(PartialSymbol<A, ?> partialSymbol, A defaultValue) { 46 public <A> Cursor<Tuple, A> getCursor(PartialSymbol<A, ?> partialSymbol, A defaultValue) {
43 return getSeed(partialSymbol).getCursor(defaultValue, nodeCount); 47 return getSeed(partialSymbol).getCursor(defaultValue, nodeCount);
44 } 48 }
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
index c7bc9fff..393a8769 100644
--- 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
@@ -11,6 +11,7 @@ import tools.refinery.store.query.literal.AbstractCallLiteral;
11import tools.refinery.store.query.literal.CallLiteral; 11import tools.refinery.store.query.literal.CallLiteral;
12import tools.refinery.store.query.literal.Literal; 12import tools.refinery.store.query.literal.Literal;
13import tools.refinery.store.query.term.Variable; 13import tools.refinery.store.query.term.Variable;
14import tools.refinery.store.query.view.AnySymbolView;
14import tools.refinery.store.reasoning.interpretation.QueryBasedRelationRewriter; 15import tools.refinery.store.reasoning.interpretation.QueryBasedRelationRewriter;
15import tools.refinery.store.reasoning.literal.Concreteness; 16import tools.refinery.store.reasoning.literal.Concreteness;
16import tools.refinery.store.reasoning.literal.Modality; 17import tools.refinery.store.reasoning.literal.Modality;
@@ -67,7 +68,7 @@ class EqualsRelationRewriter extends QueryBasedRelationRewriter {
67 }; 68 };
68 } 69 }
69 70
70 public static EqualsRelationRewriter of(UpperCardinalityView upperCardinalityView) { 71 public static EqualsRelationRewriter of(AnySymbolView upperCardinalityView) {
71 var may = Query.of("MAY_EQUALS", (builder, p1, p2) -> builder 72 var may = Query.of("MAY_EQUALS", (builder, p1, p2) -> builder
72 .clause( 73 .clause(
73 p1.isEquivalent(p2), 74 p1.isEquivalent(p2),
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
index 6917b8ed..084bf6f9 100644
--- 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
@@ -5,6 +5,7 @@
5 */ 5 */
6package tools.refinery.store.reasoning.translator.multiobject; 6package tools.refinery.store.reasoning.translator.multiobject;
7 7
8import org.jetbrains.annotations.NotNull;
8import tools.refinery.store.model.Model; 9import tools.refinery.store.model.Model;
9import tools.refinery.store.reasoning.ReasoningAdapter; 10import tools.refinery.store.reasoning.ReasoningAdapter;
10import tools.refinery.store.reasoning.refinement.PartialModelInitializer; 11import tools.refinery.store.reasoning.refinement.PartialModelInitializer;
@@ -26,8 +27,7 @@ class MultiObjectInitializer implements PartialModelInitializer {
26 27
27 @Override 28 @Override
28 public void initialize(Model model, ModelSeed modelSeed) { 29 public void initialize(Model model, ModelSeed modelSeed) {
29 var intervals = new CardinalityInterval[modelSeed.getNodeCount()]; 30 var intervals = initializeIntervals(modelSeed);
30 Arrays.fill(intervals, CardinalityIntervals.SET);
31 initializeExists(intervals, modelSeed); 31 initializeExists(intervals, modelSeed);
32 initializeEquals(intervals, modelSeed); 32 initializeEquals(intervals, modelSeed);
33 var countInterpretation = model.getInterpretation(countSymbol); 33 var countInterpretation = model.getInterpretation(countSymbol);
@@ -40,7 +40,33 @@ class MultiObjectInitializer implements PartialModelInitializer {
40 } 40 }
41 } 41 }
42 42
43 @NotNull
44 private CardinalityInterval[] initializeIntervals(ModelSeed modelSeed) {
45 var intervals = new CardinalityInterval[modelSeed.getNodeCount()];
46 if (modelSeed.containsSeed(MultiObjectTranslator.COUNT_SYMBOL)) {
47 Arrays.fill(intervals, CardinalityIntervals.ONE);
48 var cursor = modelSeed.getCursor(MultiObjectTranslator.COUNT_SYMBOL, CardinalityIntervals.ONE);
49 while (cursor.move()) {
50 int i = cursor.getKey().get(0);
51 checkNodeId(intervals, i);
52 intervals[i] = cursor.getValue();
53 }
54 } else {
55 Arrays.fill(intervals, CardinalityIntervals.SET);
56 if (!modelSeed.containsSeed(ReasoningAdapter.EXISTS_SYMBOL) ||
57 !modelSeed.containsSeed(ReasoningAdapter.EQUALS_SYMBOL)) {
58 throw new IllegalArgumentException("Seed for %s and %s is required if there is no seed for %s"
59 .formatted(ReasoningAdapter.EXISTS_SYMBOL, ReasoningAdapter.EQUALS_SYMBOL,
60 MultiObjectTranslator.COUNT_SYMBOL));
61 }
62 }
63 return intervals;
64 }
65
43 private void initializeExists(CardinalityInterval[] intervals, ModelSeed modelSeed) { 66 private void initializeExists(CardinalityInterval[] intervals, ModelSeed modelSeed) {
67 if (!modelSeed.containsSeed(ReasoningAdapter.EXISTS_SYMBOL)) {
68 return;
69 }
44 var cursor = modelSeed.getCursor(ReasoningAdapter.EXISTS_SYMBOL, TruthValue.UNKNOWN); 70 var cursor = modelSeed.getCursor(ReasoningAdapter.EXISTS_SYMBOL, TruthValue.UNKNOWN);
45 while (cursor.move()) { 71 while (cursor.move()) {
46 int i = cursor.getKey().get(0); 72 int i = cursor.getKey().get(0);
@@ -56,6 +82,9 @@ class MultiObjectInitializer implements PartialModelInitializer {
56 } 82 }
57 83
58 private void initializeEquals(CardinalityInterval[] intervals, ModelSeed modelSeed) { 84 private void initializeEquals(CardinalityInterval[] intervals, ModelSeed modelSeed) {
85 if (!modelSeed.containsSeed(ReasoningAdapter.EQUALS_SYMBOL)) {
86 return;
87 }
59 var seed = modelSeed.getSeed(ReasoningAdapter.EQUALS_SYMBOL); 88 var seed = modelSeed.getSeed(ReasoningAdapter.EQUALS_SYMBOL);
60 var cursor = seed.getCursor(TruthValue.FALSE, modelSeed.getNodeCount()); 89 var cursor = seed.getCursor(TruthValue.FALSE, modelSeed.getNodeCount());
61 while (cursor.move()) { 90 while (cursor.move()) {
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
index 0367214b..09993c80 100644
--- 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
@@ -8,11 +8,14 @@ package tools.refinery.store.reasoning.translator.multiobject;
8import tools.refinery.store.model.ModelStoreBuilder; 8import tools.refinery.store.model.ModelStoreBuilder;
9import tools.refinery.store.model.ModelStoreConfiguration; 9import tools.refinery.store.model.ModelStoreConfiguration;
10import tools.refinery.store.query.dnf.Query; 10import tools.refinery.store.query.dnf.Query;
11import tools.refinery.store.query.view.AnySymbolView;
11import tools.refinery.store.reasoning.ReasoningAdapter; 12import tools.refinery.store.reasoning.ReasoningAdapter;
12import tools.refinery.store.reasoning.ReasoningBuilder; 13import tools.refinery.store.reasoning.ReasoningBuilder;
14import tools.refinery.store.reasoning.representation.PartialFunction;
13import tools.refinery.store.reasoning.translator.PartialRelationTranslator; 15import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
14import tools.refinery.store.reasoning.translator.RoundingMode; 16import tools.refinery.store.reasoning.translator.RoundingMode;
15import tools.refinery.store.representation.Symbol; 17import tools.refinery.store.representation.Symbol;
18import tools.refinery.store.representation.cardinality.CardinalityDomain;
16import tools.refinery.store.representation.cardinality.CardinalityInterval; 19import tools.refinery.store.representation.cardinality.CardinalityInterval;
17import tools.refinery.store.representation.cardinality.UpperCardinalities; 20import tools.refinery.store.representation.cardinality.UpperCardinalities;
18import tools.refinery.store.representation.cardinality.UpperCardinality; 21import tools.refinery.store.representation.cardinality.UpperCardinality;
@@ -26,35 +29,37 @@ import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityT
26import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.greaterEq; 29import static tools.refinery.store.query.term.uppercardinality.UpperCardinalityTerms.greaterEq;
27 30
28public class MultiObjectTranslator implements ModelStoreConfiguration { 31public class MultiObjectTranslator implements ModelStoreConfiguration {
29 private final Symbol<CardinalityInterval> countSymbol = Symbol.of("COUNT", 1, CardinalityInterval.class, 32 private static final Symbol<CardinalityInterval> COUNT_STORAGE = Symbol.of("COUNT", 1, CardinalityInterval.class,
30 null); 33 null);
31 private final LowerCardinalityView lowerCardinalityView = new LowerCardinalityView(countSymbol); 34 public static final AnySymbolView LOWER_CARDINALITY_VIEW = new LowerCardinalityView(COUNT_STORAGE);
32 private final UpperCardinalityView upperCardinalityView = new UpperCardinalityView(countSymbol); 35 public static final AnySymbolView UPPER_CARDINALITY_VIEW = new UpperCardinalityView(COUNT_STORAGE);
36 public static final PartialFunction<CardinalityInterval, Integer> COUNT_SYMBOL = new PartialFunction<>("COUNT", 1,
37 CardinalityDomain.INSTANCE);
33 38
34 @Override 39 @Override
35 public void apply(ModelStoreBuilder storeBuilder) { 40 public void apply(ModelStoreBuilder storeBuilder) {
36 storeBuilder.symbol(countSymbol); 41 storeBuilder.symbol(COUNT_STORAGE);
37 42
38 storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EXISTS_SYMBOL) 43 storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EXISTS_SYMBOL)
39 .may(Query.of("MAY_EXISTS", (builder, p1) -> builder 44 .may(Query.of("MAY_EXISTS", (builder, p1) -> builder
40 .clause(UpperCardinality.class, upper -> List.of( 45 .clause(UpperCardinality.class, upper -> List.of(
41 upperCardinalityView.call(p1, upper), 46 UPPER_CARDINALITY_VIEW.call(p1, upper),
42 check(greaterEq(upper, constant(UpperCardinalities.ONE))) 47 check(greaterEq(upper, constant(UpperCardinalities.ONE)))
43 )))) 48 ))))
44 .must(Query.of("MUST_EXISTS", (builder, p1) -> builder 49 .must(Query.of("MUST_EXISTS", (builder, p1) -> builder
45 .clause(Integer.class, lower -> List.of( 50 .clause(Integer.class, lower -> List.of(
46 lowerCardinalityView.call(p1, lower), 51 LOWER_CARDINALITY_VIEW.call(p1, lower),
47 check(greaterEq(lower, constant(1))) 52 check(greaterEq(lower, constant(1)))
48 )))) 53 ))))
49 .roundingMode(RoundingMode.PREFER_FALSE) 54 .roundingMode(RoundingMode.PREFER_FALSE)
50 .refiner(ExistsRefiner.of(countSymbol))); 55 .refiner(ExistsRefiner.of(COUNT_STORAGE)));
51 56
52 storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EQUALS_SYMBOL) 57 storeBuilder.with(PartialRelationTranslator.of(ReasoningAdapter.EQUALS_SYMBOL)
53 .rewriter(EqualsRelationRewriter.of(upperCardinalityView)) 58 .rewriter(EqualsRelationRewriter.of(UPPER_CARDINALITY_VIEW))
54 .refiner(EqualsRefiner.of(countSymbol))); 59 .refiner(EqualsRefiner.of(COUNT_STORAGE)));
55 60
56 var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class); 61 var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class);
57 reasoningBuilder.initializer(new MultiObjectInitializer(countSymbol)); 62 reasoningBuilder.initializer(new MultiObjectInitializer(COUNT_STORAGE));
58 reasoningBuilder.storageRefiner(countSymbol, MultiObjectStorageRefiner::new); 63 reasoningBuilder.storageRefiner(COUNT_STORAGE, MultiObjectStorageRefiner::new);
59 } 64 }
60} 65}
diff --git a/subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/ConcreteCountTest.java b/subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/ConcreteCountTest.java
new file mode 100644
index 00000000..21111d7c
--- /dev/null
+++ b/subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/ConcreteCountTest.java
@@ -0,0 +1,324 @@
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;
7
8import org.junit.jupiter.api.Test;
9import tools.refinery.store.model.ModelStore;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.dnf.Query;
12import tools.refinery.store.query.resultset.ResultSet;
13import tools.refinery.store.query.term.Variable;
14import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
15import tools.refinery.store.reasoning.literal.Concreteness;
16import tools.refinery.store.reasoning.literal.CountLowerBoundLiteral;
17import tools.refinery.store.reasoning.literal.CountUpperBoundLiteral;
18import tools.refinery.store.reasoning.representation.PartialRelation;
19import tools.refinery.store.reasoning.seed.ModelSeed;
20import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
21import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator;
22import tools.refinery.store.representation.Symbol;
23import tools.refinery.store.representation.TruthValue;
24import tools.refinery.store.representation.cardinality.CardinalityIntervals;
25import tools.refinery.store.representation.cardinality.UpperCardinalities;
26import tools.refinery.store.representation.cardinality.UpperCardinality;
27import tools.refinery.store.tuple.Tuple;
28
29import java.util.List;
30
31import static org.hamcrest.MatcherAssert.assertThat;
32import static org.hamcrest.Matchers.is;
33import static tools.refinery.store.query.literal.Literals.not;
34import static tools.refinery.store.reasoning.literal.PartialLiterals.must;
35
36class ConcreteCountTest {
37 private static final PartialRelation person = new PartialRelation("Person", 1);
38 private static final PartialRelation friend = new PartialRelation("friend", 2);
39
40 @Test
41 void lowerBoundZeroTest() {
42 var query = Query.of("LowerBound", Integer.class, (builder, p1, p2, output) -> builder.clause(
43 must(person.call(p1)),
44 must(person.call(p2)),
45 new CountLowerBoundLiteral(output, Concreteness.PARTIAL, friend, List.of(p1, p2))
46 ));
47
48 var modelSeed = ModelSeed.builder(2)
49 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
50 .put(Tuple.of(0), CardinalityIntervals.atLeast(3))
51 .put(Tuple.of(1), CardinalityIntervals.atMost(7)))
52 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
53 .seed(friend, builder -> builder
54 .put(Tuple.of(0, 1), TruthValue.TRUE)
55 .put(Tuple.of(1, 0), TruthValue.UNKNOWN)
56 .put(Tuple.of(1, 1), TruthValue.ERROR))
57 .build();
58
59 var resultSet = getResultSet(query, modelSeed);
60 assertThat(resultSet.get(Tuple.of(0, 0)), is(0));
61 assertThat(resultSet.get(Tuple.of(0, 1)), is(1));
62 assertThat(resultSet.get(Tuple.of(1, 0)), is(0));
63 assertThat(resultSet.get(Tuple.of(1, 1)), is(1));
64 }
65
66 @Test
67 void upperBoundZeroTest() {
68 var query = Query.of("UpperBound", UpperCardinality.class, (builder, p1, p2, output) -> builder.clause(
69 must(person.call(p1)),
70 must(person.call(p2)),
71 new CountUpperBoundLiteral(output, Concreteness.PARTIAL, friend, List.of(p1, p2))
72 ));
73
74 var modelSeed = ModelSeed.builder(2)
75 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
76 .put(Tuple.of(0), CardinalityIntervals.atLeast(3))
77 .put(Tuple.of(1), CardinalityIntervals.atMost(7)))
78 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
79 .seed(friend, builder -> builder
80 .put(Tuple.of(0, 1), TruthValue.TRUE)
81 .put(Tuple.of(1, 0), TruthValue.UNKNOWN)
82 .put(Tuple.of(1, 1), TruthValue.ERROR))
83 .build();
84
85 var resultSet = getResultSet(query, modelSeed);
86 assertThat(resultSet.get(Tuple.of(0, 0)), is(UpperCardinalities.ZERO));
87 assertThat(resultSet.get(Tuple.of(0, 1)), is(UpperCardinalities.ONE));
88 assertThat(resultSet.get(Tuple.of(1, 0)), is(UpperCardinalities.ONE));
89 assertThat(resultSet.get(Tuple.of(1, 1)), is(UpperCardinalities.ZERO));
90 }
91
92 @Test
93 void lowerBoundOneTest() {
94 var query = Query.of("LowerBound", Integer.class, (builder, p1, output) -> builder.clause(
95 must(person.call(p1)),
96 new CountLowerBoundLiteral(output, Concreteness.PARTIAL, friend, List.of(p1, Variable.of()))
97 ));
98
99 var modelSeed = ModelSeed.builder(4)
100 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
101 .reducedValue(CardinalityIntervals.ONE)
102 .put(Tuple.of(1), CardinalityIntervals.atLeast(3))
103 .put(Tuple.of(2), CardinalityIntervals.atMost(7)))
104 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
105 .seed(friend, builder -> builder
106 .put(Tuple.of(0, 1), TruthValue.TRUE)
107 .put(Tuple.of(0, 2), TruthValue.TRUE)
108 .put(Tuple.of(0, 3), TruthValue.TRUE)
109 .put(Tuple.of(1, 0), TruthValue.TRUE)
110 .put(Tuple.of(1, 2), TruthValue.UNKNOWN)
111 .put(Tuple.of(1, 3), TruthValue.UNKNOWN)
112 .put(Tuple.of(2, 0), TruthValue.TRUE)
113 .put(Tuple.of(2, 1), TruthValue.ERROR))
114 .build();
115
116 var resultSet = getResultSet(query, modelSeed);
117 assertThat(resultSet.get(Tuple.of(0)), is(4));
118 assertThat(resultSet.get(Tuple.of(1)), is(1));
119 assertThat(resultSet.get(Tuple.of(2)), is(4));
120 assertThat(resultSet.get(Tuple.of(3)), is(0));
121 }
122
123 @Test
124 void upperBoundOneTest() {
125 var query = Query.of("UpperBound", UpperCardinality.class, (builder, p1, output) -> builder.clause(
126 must(person.call(p1)),
127 new CountUpperBoundLiteral(output, Concreteness.PARTIAL, friend, List.of(p1, Variable.of()))
128 ));
129
130 var modelSeed = ModelSeed.builder(4)
131 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
132 .reducedValue(CardinalityIntervals.ONE)
133 .put(Tuple.of(1), CardinalityIntervals.atLeast(3))
134 .put(Tuple.of(2), CardinalityIntervals.atMost(7)))
135 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
136 .seed(friend, builder -> builder
137 .put(Tuple.of(0, 1), TruthValue.TRUE)
138 .put(Tuple.of(0, 2), TruthValue.TRUE)
139 .put(Tuple.of(0, 3), TruthValue.TRUE)
140 .put(Tuple.of(1, 0), TruthValue.TRUE)
141 .put(Tuple.of(1, 2), TruthValue.UNKNOWN)
142 .put(Tuple.of(1, 3), TruthValue.UNKNOWN)
143 .put(Tuple.of(2, 0), TruthValue.TRUE)
144 .put(Tuple.of(2, 1), TruthValue.ERROR))
145 .build();
146
147 var resultSet = getResultSet(query, modelSeed);
148 assertThat(resultSet.get(Tuple.of(0)), is(UpperCardinalities.UNBOUNDED));
149 assertThat(resultSet.get(Tuple.of(1)), is(UpperCardinalities.atMost(9)));
150 assertThat(resultSet.get(Tuple.of(2)), is(UpperCardinalities.ONE));
151 assertThat(resultSet.get(Tuple.of(3)), is(UpperCardinalities.ZERO));
152 }
153
154 @Test
155 void lowerBoundTwoTest() {
156 var subQuery = Query.of("SubQuery", (builder, p1, p2, p3) -> builder.clause(
157 friend.call(p1, p2),
158 friend.call(p1, p3),
159 friend.call(p2, p3)
160 ));
161 var query = Query.of("LowerBound", Integer.class, (builder, p1, output) -> builder.clause(
162 must(person.call(p1)),
163 new CountLowerBoundLiteral(output, Concreteness.PARTIAL, subQuery.getDnf(),
164 List.of(p1, Variable.of(), Variable.of()))
165 ));
166
167 var modelSeed = ModelSeed.builder(4)
168 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
169 .reducedValue(CardinalityIntervals.ONE)
170 .put(Tuple.of(0), CardinalityIntervals.between(5, 9))
171 .put(Tuple.of(1), CardinalityIntervals.atLeast(3))
172 .put(Tuple.of(2), CardinalityIntervals.atMost(7)))
173 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
174 .seed(friend, builder -> builder
175 .put(Tuple.of(0, 1), TruthValue.TRUE)
176 .put(Tuple.of(0, 2), TruthValue.TRUE)
177 .put(Tuple.of(0, 3), TruthValue.TRUE)
178 .put(Tuple.of(1, 0), TruthValue.TRUE)
179 .put(Tuple.of(1, 2), TruthValue.TRUE)
180 .put(Tuple.of(1, 3), TruthValue.TRUE)
181 .put(Tuple.of(2, 0), TruthValue.TRUE)
182 .put(Tuple.of(2, 1), TruthValue.ERROR))
183 .build();
184
185 var resultSet = getResultSet(query, modelSeed);
186 assertThat(resultSet.get(Tuple.of(0)), is(3));
187 assertThat(resultSet.get(Tuple.of(1)), is(5));
188 assertThat(resultSet.get(Tuple.of(2)), is(30));
189 assertThat(resultSet.get(Tuple.of(3)), is(0));
190 }
191
192 @Test
193 void upperBoundTwoTest() {
194 var subQuery = Query.of("SubQuery", (builder, p1, p2, p3) -> builder.clause(
195 friend.call(p1, p2),
196 friend.call(p1, p3),
197 friend.call(p2, p3)
198 ));
199 var query = Query.of("UpperBound", UpperCardinality.class, (builder, p1, output) -> builder.clause(
200 must(person.call(p1)),
201 new CountUpperBoundLiteral(output, Concreteness.PARTIAL, subQuery.getDnf(),
202 List.of(p1, Variable.of(), Variable.of()))
203 ));
204
205 var modelSeed = ModelSeed.builder(4)
206 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
207 .reducedValue(CardinalityIntervals.ONE)
208 .put(Tuple.of(0), CardinalityIntervals.between(5, 9))
209 .put(Tuple.of(1), CardinalityIntervals.atLeast(3))
210 .put(Tuple.of(2), CardinalityIntervals.atMost(7)))
211 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
212 .seed(friend, builder -> builder
213 .put(Tuple.of(0, 1), TruthValue.TRUE)
214 .put(Tuple.of(0, 2), TruthValue.TRUE)
215 .put(Tuple.of(0, 3), TruthValue.TRUE)
216 .put(Tuple.of(1, 0), TruthValue.TRUE)
217 .put(Tuple.of(1, 2), TruthValue.UNKNOWN)
218 .put(Tuple.of(1, 3), TruthValue.UNKNOWN)
219 .put(Tuple.of(2, 0), TruthValue.TRUE)
220 .put(Tuple.of(2, 1), TruthValue.ERROR))
221 .build();
222
223 var resultSet = getResultSet(query, modelSeed);
224 assertThat(resultSet.get(Tuple.of(0)), is(UpperCardinalities.UNBOUNDED));
225 assertThat(resultSet.get(Tuple.of(1)), is(UpperCardinalities.atMost(135)));
226 assertThat(resultSet.get(Tuple.of(2)), is(UpperCardinalities.ZERO));
227 assertThat(resultSet.get(Tuple.of(3)), is(UpperCardinalities.ZERO));
228 }
229
230 @Test
231 void lowerBoundDiagonalTest() {
232 var subQuery = Query.of("SubQuery", (builder, p1, p2, p3) -> builder.clause(
233 friend.call(p1, p2),
234 friend.call(p1, p3),
235 not(friend.call(p2, p3))
236 ));
237 var query = Query.of("LowerBound", Integer.class, (builder, p1, output) -> builder.clause(v1 -> List.of(
238 must(person.call(p1)),
239 new CountLowerBoundLiteral(output, Concreteness.PARTIAL, subQuery.getDnf(), List.of(p1, v1, v1))
240 )));
241
242 var modelSeed = ModelSeed.builder(4)
243 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
244 .reducedValue(CardinalityIntervals.ONE)
245 .put(Tuple.of(0), CardinalityIntervals.between(5, 9))
246 .put(Tuple.of(1), CardinalityIntervals.atLeast(3))
247 .put(Tuple.of(2), CardinalityIntervals.atMost(7)))
248 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
249 .seed(friend, builder -> builder
250 .put(Tuple.of(0, 1), TruthValue.TRUE)
251 .put(Tuple.of(0, 2), TruthValue.TRUE)
252 .put(Tuple.of(0, 3), TruthValue.TRUE)
253 .put(Tuple.of(1, 0), TruthValue.TRUE)
254 .put(Tuple.of(1, 2), TruthValue.UNKNOWN)
255 .put(Tuple.of(1, 3), TruthValue.UNKNOWN)
256 .put(Tuple.of(2, 0), TruthValue.TRUE)
257 .put(Tuple.of(2, 1), TruthValue.ERROR))
258 .build();
259
260 var resultSet = getResultSet(query, modelSeed);
261 assertThat(resultSet.get(Tuple.of(0)), is(4));
262 assertThat(resultSet.get(Tuple.of(1)), is(5));
263 assertThat(resultSet.get(Tuple.of(2)), is(8));
264 assertThat(resultSet.get(Tuple.of(3)), is(0));
265 }
266
267 @Test
268 void upperBoundDiagonalTest() {
269 var subQuery = Query.of("SubQuery", (builder, p1, p2, p3) -> builder.clause(
270 friend.call(p1, p2),
271 friend.call(p1, p3),
272 not(friend.call(p2, p3))
273 ));
274 var query = Query.of("UpperBound", UpperCardinality.class, (builder, p1, output) -> builder
275 .clause(v1 -> List.of(
276 must(person.call(p1)),
277 new CountUpperBoundLiteral(output, Concreteness.PARTIAL, subQuery.getDnf(),
278 List.of(p1, v1, v1))
279 )));
280
281 var modelSeed = ModelSeed.builder(4)
282 .seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
283 .reducedValue(CardinalityIntervals.ONE)
284 .put(Tuple.of(0), CardinalityIntervals.between(5, 9))
285 .put(Tuple.of(1), CardinalityIntervals.atLeast(3))
286 .put(Tuple.of(2), CardinalityIntervals.atMost(7)))
287 .seed(person, builder -> builder.reducedValue(TruthValue.TRUE))
288 .seed(friend, builder -> builder
289 .put(Tuple.of(0, 1), TruthValue.TRUE)
290 .put(Tuple.of(0, 2), TruthValue.TRUE)
291 .put(Tuple.of(0, 3), TruthValue.TRUE)
292 .put(Tuple.of(1, 0), TruthValue.TRUE)
293 .put(Tuple.of(1, 2), TruthValue.UNKNOWN)
294 .put(Tuple.of(1, 3), TruthValue.UNKNOWN)
295 .put(Tuple.of(2, 0), TruthValue.TRUE)
296 .put(Tuple.of(2, 1), TruthValue.ERROR))
297 .build();
298
299 var resultSet = getResultSet(query, modelSeed);
300 assertThat(resultSet.get(Tuple.of(0)), is(UpperCardinalities.UNBOUNDED));
301 assertThat(resultSet.get(Tuple.of(1)), is(UpperCardinalities.atMost(17)));
302 assertThat(resultSet.get(Tuple.of(2)), is(UpperCardinalities.atMost(9)));
303 assertThat(resultSet.get(Tuple.of(3)), is(UpperCardinalities.ZERO));
304 }
305
306 private static <T> ResultSet<T> getResultSet(Query<T> query, ModelSeed modelSeed) {
307 var personStorage = Symbol.of("Person", 1, TruthValue.class, TruthValue.FALSE);
308 var friendStorage = Symbol.of("friend", 2, TruthValue.class, TruthValue.FALSE);
309
310 var store = ModelStore.builder()
311 .with(ViatraModelQueryAdapter.builder()
312 .query(query))
313 .with(ReasoningAdapter.builder())
314 .with(new MultiObjectTranslator())
315 .with(PartialRelationTranslator.of(person)
316 .symbol(personStorage))
317 .with(PartialRelationTranslator.of(friend)
318 .symbol(friendStorage))
319 .build();
320
321 var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed);
322 return model.getAdapter(ModelQueryAdapter.class).getResultSet(query);
323 }
324}