aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-reasoning/src/main
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-06-26 19:38:03 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-06-29 02:23:01 +0200
commitc42d391998eca46704c8699c90efd3c357442fb0 (patch)
treea82e7dd884b010936470111ec1be3e5aea19a213 /subprojects/store-reasoning/src/main
parentfeat: remove contradictory calls in Dnf builder (diff)
downloadrefinery-c42d391998eca46704c8699c90efd3c357442fb0.tar.gz
refinery-c42d391998eca46704c8699c90efd3c357442fb0.tar.zst
refinery-c42d391998eca46704c8699c90efd3c357442fb0.zip
feat: Dnf lifting
Support for count and aggregation is still missing.
Diffstat (limited to 'subprojects/store-reasoning/src/main')
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java3
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java221
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java16
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Concreteness.java18
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java39
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Modality.java4
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java4
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialSymbol.java8
8 files changed, 201 insertions, 112 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java
index 6d5d6f89..66e809f7 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java
@@ -13,7 +13,8 @@ import tools.refinery.store.reasoning.representation.PartialRelation;
13import tools.refinery.store.reasoning.representation.PartialSymbol; 13import tools.refinery.store.reasoning.representation.PartialSymbol;
14 14
15public interface ReasoningAdapter extends ModelAdapter { 15public interface ReasoningAdapter extends ModelAdapter {
16 PartialRelation EXISTS = new PartialRelation("exists", 1); 16 PartialRelation EXISTS = PartialSymbol.of("exists", 1);
17 PartialRelation EQUALS = PartialSymbol.of("equals", 2);
17 18
18 @Override 19 @Override
19 ReasoningStoreAdapter getStoreAdapter(); 20 ReasoningStoreAdapter getStoreAdapter();
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java
index ac41d170..8bbb75d1 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java
@@ -5,124 +5,181 @@
5 */ 5 */
6package tools.refinery.store.reasoning.lifting; 6package tools.refinery.store.reasoning.lifting;
7 7
8import org.jetbrains.annotations.Nullable; 8import tools.refinery.store.query.Constraint;
9import tools.refinery.store.query.dnf.Dnf; 9import tools.refinery.store.query.dnf.Dnf;
10import tools.refinery.store.query.dnf.DnfBuilder;
11import tools.refinery.store.query.dnf.DnfClause; 10import tools.refinery.store.query.dnf.DnfClause;
12import tools.refinery.store.query.literal.CallLiteral; 11import tools.refinery.store.query.equality.DnfEqualityChecker;
13import tools.refinery.store.query.literal.CallPolarity; 12import tools.refinery.store.query.literal.*;
14import tools.refinery.store.query.literal.Literal;
15import tools.refinery.store.query.term.NodeVariable; 13import tools.refinery.store.query.term.NodeVariable;
14import tools.refinery.store.query.term.ParameterDirection;
16import tools.refinery.store.query.term.Variable; 15import tools.refinery.store.query.term.Variable;
17import tools.refinery.store.reasoning.ReasoningAdapter; 16import tools.refinery.store.reasoning.ReasoningAdapter;
17import tools.refinery.store.reasoning.literal.Concreteness;
18import tools.refinery.store.reasoning.literal.ModalConstraint; 18import tools.refinery.store.reasoning.literal.ModalConstraint;
19import tools.refinery.store.reasoning.literal.Modality; 19import tools.refinery.store.reasoning.literal.Modality;
20import tools.refinery.store.reasoning.literal.PartialLiterals;
21import tools.refinery.store.util.CycleDetectingMapper;
22 20
23import java.util.ArrayList; 21import java.util.*;
24import java.util.LinkedHashSet; 22import java.util.stream.Collectors;
25import java.util.List;
26 23
27public class DnfLifter { 24public class DnfLifter {
28 private final CycleDetectingMapper<ModalDnf, Dnf> mapper = new CycleDetectingMapper<>(ModalDnf::toString, 25 private final Map<ModalDnf, Dnf> cache = new HashMap<>();
29 this::doLift);
30 26
31 public Dnf lift(Modality modality, Dnf query) { 27 public Dnf lift(Modality modality, Concreteness concreteness, Dnf dnf) {
32 return mapper.map(new ModalDnf(modality, query)); 28 return cache.computeIfAbsent(new ModalDnf(modality, concreteness, dnf), this::doLift);
33 } 29 }
34 30
35 private Dnf doLift(ModalDnf modalDnf) { 31 private Dnf doLift(ModalDnf modalDnf) {
36 var modality = modalDnf.modality(); 32 var modality = modalDnf.modality();
33 var concreteness = modalDnf.concreteness();
37 var dnf = modalDnf.dnf(); 34 var dnf = modalDnf.dnf();
38 var builder = Dnf.builder(); 35 var builder = Dnf.builder("%s#%s#%s".formatted(dnf.name(), modality, concreteness));
39 builder.symbolicParameters(dnf.getSymbolicParameters()); 36 builder.symbolicParameters(dnf.getSymbolicParameters());
40 boolean changed = false; 37 builder.functionalDependencies(dnf.getFunctionalDependencies());
41 for (var clause : dnf.getClauses()) { 38 for (var clause : dnf.getClauses()) {
42 if (liftClause(modality, dnf, clause, builder)) { 39 builder.clause(liftClause(modality, concreteness, dnf, clause));
43 changed = true;
44 }
45 } 40 }
46 if (changed) { 41 var liftedDnf = builder.build();
47 return builder.build(); 42 if (dnf.equalsWithSubstitution(DnfEqualityChecker.DEFAULT, liftedDnf)) {
43 return dnf;
48 } 44 }
49 return dnf; 45 return liftedDnf;
50 } 46 }
51 47
52 private boolean liftClause(Modality modality, Dnf originalDnf, DnfClause clause, DnfBuilder builder) { 48 private List<Literal> liftClause(Modality modality, Concreteness concreteness, Dnf dnf, DnfClause clause) {
53 boolean changed = false; 49 var liftedLiterals = new ArrayList<Literal>();
54 var quantifiedVariables = getQuantifiedDataVariables(originalDnf, clause); 50 for (var literal : clause.literals()) {
55 var literals = clause.literals(); 51 var liftedLiteral = liftLiteral(modality, concreteness, clause, literal);
56 var liftedLiterals = new ArrayList<Literal>(literals.size());
57 for (var literal : literals) {
58 Literal liftedLiteral = liftLiteral(modality, literal);
59 if (liftedLiteral == null) {
60 liftedLiteral = literal;
61 } else {
62 changed = true;
63 }
64 liftedLiterals.add(liftedLiteral); 52 liftedLiterals.add(liftedLiteral);
65 var variable = isExistsLiteralForVariable(modality, liftedLiteral);
66 if (variable != null) {
67 // If we already quantify over the existence of the variable with the expected modality,
68 // we don't need to insert quantification manually.
69 quantifiedVariables.remove(variable);
70 }
71 } 53 }
54 var quantifiedVariables = getQuantifiedNodeVariables(dnf, clause);
55 var existsConstraint = ModalConstraint.of(modality, concreteness, ReasoningAdapter.EXISTS);
72 for (var quantifiedVariable : quantifiedVariables) { 56 for (var quantifiedVariable : quantifiedVariables) {
73 // Quantify over data variables that are not already quantified with the expected modality. 57 liftedLiterals.add(existsConstraint.call(quantifiedVariable));
74 liftedLiterals.add(new CallLiteral(CallPolarity.POSITIVE,
75 new ModalConstraint(modality, ReasoningAdapter.EXISTS), List.of(quantifiedVariable)));
76 } 58 }
77 builder.clause(liftedLiterals); 59 return liftedLiterals;
78 return changed || !quantifiedVariables.isEmpty();
79 } 60 }
80 61
81 private static LinkedHashSet<Variable> getQuantifiedDataVariables(Dnf originalDnf, DnfClause clause) { 62 private Literal liftLiteral(Modality modality, Concreteness concreteness, DnfClause clause, Literal literal) {
82 var quantifiedVariables = new LinkedHashSet<>(clause.positiveVariables()); 63 if (literal instanceof CallLiteral callLiteral) {
83 for (var symbolicParameter : originalDnf.getSymbolicParameters()) { 64 return liftCallLiteral(modality, concreteness, clause, callLiteral);
84 // The existence of parameters will be checked outside this DNF. 65 } else if (literal instanceof EquivalenceLiteral equivalenceLiteral) {
85 quantifiedVariables.remove(symbolicParameter.getVariable()); 66 return liftEquivalenceLiteral(modality, concreteness, equivalenceLiteral);
67 } else if (literal instanceof ConstantLiteral ||
68 literal instanceof AssignLiteral<?> ||
69 literal instanceof CheckLiteral) {
70 return literal;
71 } else if (literal instanceof CountLiteral) {
72 throw new IllegalArgumentException("Count literal %s cannot be lifted".formatted(literal));
73 } else if (literal instanceof AggregationLiteral<?,?>) {
74 throw new IllegalArgumentException("Aggregation literal %s cannot be lifted".formatted(literal));
75 } else {
76 throw new IllegalArgumentException("Unknown literal to lift: " + literal);
86 } 77 }
87 quantifiedVariables.removeIf(variable -> !(variable instanceof NodeVariable));
88 return quantifiedVariables;
89 } 78 }
90 79
91 @Nullable 80 private Literal liftCallLiteral(Modality modality, Concreteness concreteness, DnfClause clause,
92 private Variable isExistsLiteralForVariable(Modality modality, Literal literal) { 81 CallLiteral callLiteral) {
93 if (literal instanceof CallLiteral callLiteral && 82 Constraint target = callLiteral.getTarget();
94 callLiteral.getPolarity() == CallPolarity.POSITIVE && 83 var polarity = callLiteral.getPolarity();
95 callLiteral.getTarget() instanceof ModalConstraint modalConstraint && 84 var arguments = callLiteral.getArguments();
96 modalConstraint.modality() == modality && 85 return switch (polarity) {
97 modalConstraint.constraint().equals(ReasoningAdapter.EXISTS)) { 86 case POSITIVE -> ModalConstraint.of(modality, concreteness, target).call(
98 return callLiteral.getArguments().get(0); 87 CallPolarity.POSITIVE, arguments);
88 case NEGATIVE -> {
89 var callModality = modality.negate();
90 boolean needsHelper = !polarity.isPositive() &&
91 !callLiteral.getPrivateVariables(clause.positiveVariables()).isEmpty();
92 if (needsHelper) {
93 yield callNegationHelper(callModality, concreteness, clause, callLiteral, target);
94 }
95 yield ModalConstraint.of(callModality, concreteness, target).call(
96 CallPolarity.NEGATIVE, arguments);
97 }
98 case TRANSITIVE -> createTransitiveHelper(modality, concreteness, target).call(
99 CallPolarity.POSITIVE, arguments);
100 };
101 }
102
103 private Literal callNegationHelper(Modality modality, Concreteness concreteness, DnfClause clause,
104 CallLiteral callLiteral, Constraint target) {
105 var builder = Dnf.builder();
106 var originalArguments = callLiteral.getArguments();
107 var uniqueOriginalArguments = List.copyOf(new LinkedHashSet<>(originalArguments));
108
109 var alwaysInputVariables = callLiteral.getInputVariables(Set.of());
110 for (var variable : uniqueOriginalArguments) {
111 var direction = alwaysInputVariables.contains(variable) ? ParameterDirection.IN : ParameterDirection.OUT;
112 builder.parameter(variable, direction);
99 } 113 }
100 return null; 114
115 var literals = new ArrayList<Literal>();
116 var liftedConstraint = ModalConstraint.of(modality, concreteness, target);
117 literals.add(liftedConstraint.call(CallPolarity.POSITIVE, originalArguments));
118
119 var privateVariables = callLiteral.getPrivateVariables(clause.positiveVariables());
120 var existsConstraint = ModalConstraint.of(modality, concreteness, ReasoningAdapter.EXISTS);
121 for (var variable : uniqueOriginalArguments) {
122 if (privateVariables.contains(variable)) {
123 literals.add(existsConstraint.call(variable));
124 }
125 }
126
127 builder.clause(literals);
128 var liftedTarget = builder.build();
129 return liftedTarget.call(CallPolarity.NEGATIVE, uniqueOriginalArguments);
101 } 130 }
102 131
103 @Nullable 132 private Dnf createTransitiveHelper(Modality modality, Concreteness concreteness, Constraint target) {
104 private Literal liftLiteral(Modality modality, Literal literal) { 133 var liftedTarget = ModalConstraint.of(modality, concreteness, target);
105 if (!(literal instanceof CallLiteral callLiteral)) { 134 var existsConstraint = ModalConstraint.of(modality, concreteness, ReasoningAdapter.EXISTS);
106 return null; 135 var existingEndHelperName = "%s#exisitingEnd#%s#%s".formatted(target.name(), modality, concreteness);
136 var existingEndHelper = Dnf.of(existingEndHelperName, builder -> {
137 var start = builder.parameter("start");
138 var end = builder.parameter("end");
139 builder.clause(
140 liftedTarget.call(start, end),
141 existsConstraint.call(end)
142 );
143 });
144 var transitiveHelperName = "%s#transitive#%s#%s".formatted(target.name(), modality, concreteness);
145 return Dnf.of(transitiveHelperName, builder -> {
146 var start = builder.parameter("start");
147 var end = builder.parameter("end");
148 builder.clause(liftedTarget.call(start, end));
149 builder.clause(middle -> List.of(
150 existingEndHelper.callTransitive(start, middle),
151 liftedTarget.call(middle, end)
152 ));
153 });
154 }
155
156 private Literal liftEquivalenceLiteral(Modality modality, Concreteness concreteness,
157 EquivalenceLiteral equivalenceLiteral) {
158 if (equivalenceLiteral.isPositive()) {
159 return ModalConstraint.of(modality, concreteness, ReasoningAdapter.EQUALS).call(
160 CallPolarity.POSITIVE, equivalenceLiteral.getLeft(), equivalenceLiteral.getRight());
107 } 161 }
108 var target = callLiteral.getTarget(); 162 return ModalConstraint.of(modality.negate(), concreteness, ReasoningAdapter.EQUALS).call(
109 if (target instanceof ModalConstraint modalTarget) { 163 CallPolarity.NEGATIVE, equivalenceLiteral.getLeft(), equivalenceLiteral.getRight());
110 var actualTarget = modalTarget.constraint(); 164 }
111 if (actualTarget instanceof Dnf dnf) { 165
112 var targetModality = modalTarget.modality(); 166 private static Set<NodeVariable> getQuantifiedNodeVariables(Dnf dnf, DnfClause clause) {
113 var liftedTarget = lift(targetModality, dnf); 167 var quantifiedVariables = clause.positiveVariables().stream()
114 return new CallLiteral(callLiteral.getPolarity(), liftedTarget, callLiteral.getArguments()); 168 .filter(Variable::isNodeVariable)
169 .map(Variable::asNodeVariable)
170 .collect(Collectors.toCollection(HashSet::new));
171 for (var symbolicParameter : dnf.getSymbolicParameters()) {
172 if (symbolicParameter.getVariable() instanceof NodeVariable nodeParameter) {
173 quantifiedVariables.remove(nodeParameter);
115 } 174 }
116 // No more lifting to be done, pass any modal call to a partial symbol through. 175 }
117 return null; 176 return quantifiedVariables;
118 } else if (target instanceof Dnf dnf) { 177 }
119 var polarity = callLiteral.getPolarity(); 178
120 var liftedTarget = lift(modality.commute(polarity), dnf); 179 private record ModalDnf(Modality modality, Concreteness concreteness, Dnf dnf) {
121 // Use == instead of equals(), because lift will return the same object by reference is there are no 180 @Override
122 // changes made during lifting. 181 public String toString() {
123 return liftedTarget == target ? null : new CallLiteral(polarity, liftedTarget, callLiteral.getArguments()); 182 return "%s %s %s".formatted(modality, concreteness, dnf.name());
124 } else {
125 return PartialLiterals.addModality(callLiteral, modality);
126 } 183 }
127 } 184 }
128} 185}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java
deleted file mode 100644
index 16fb8fbf..00000000
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java
+++ /dev/null
@@ -1,16 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.reasoning.lifting;
7
8import tools.refinery.store.query.dnf.Dnf;
9import tools.refinery.store.reasoning.literal.Modality;
10
11record ModalDnf(Modality modality, Dnf dnf) {
12 @Override
13 public String toString() {
14 return "%s %s".formatted(modality, dnf.name());
15 }
16}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Concreteness.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Concreteness.java
new file mode 100644
index 00000000..43ac5904
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Concreteness.java
@@ -0,0 +1,18 @@
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 java.util.Locale;
9
10public enum Concreteness {
11 PARTIAL,
12 CANDIDATE;
13
14 @Override
15 public String toString() {
16 return name().toLowerCase(Locale.ROOT);
17 }
18}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java
index 4e5a6099..6e0e91e1 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java
@@ -9,15 +9,25 @@ import tools.refinery.store.query.Constraint;
9import tools.refinery.store.query.equality.LiteralEqualityHelper; 9import tools.refinery.store.query.equality.LiteralEqualityHelper;
10import tools.refinery.store.query.literal.Reduction; 10import tools.refinery.store.query.literal.Reduction;
11import tools.refinery.store.query.term.Parameter; 11import tools.refinery.store.query.term.Parameter;
12import tools.refinery.store.query.view.AnySymbolView;
12 13
13import java.util.List; 14import java.util.List;
14 15
15public record ModalConstraint(Modality modality, Constraint constraint) implements Constraint { 16public record ModalConstraint(Modality modality, Concreteness concreteness, Constraint constraint)
16 private static final String FORMAT = "%s %s"; 17 implements Constraint {
18 public ModalConstraint {
19 if (constraint instanceof AnySymbolView || constraint instanceof ModalConstraint) {
20 throw new IllegalArgumentException("Already concrete constraints cannot be abstracted");
21 }
22 }
23
24 public ModalConstraint(Modality modality, Constraint constraint) {
25 this(modality, Concreteness.PARTIAL, constraint);
26 }
17 27
18 @Override 28 @Override
19 public String name() { 29 public String name() {
20 return FORMAT.formatted(modality, constraint.name()); 30 return formatName(constraint.name());
21 } 31 }
22 32
23 @Override 33 @Override
@@ -36,16 +46,33 @@ public record ModalConstraint(Modality modality, Constraint constraint) implemen
36 return false; 46 return false;
37 } 47 }
38 var otherModalConstraint = (ModalConstraint) other; 48 var otherModalConstraint = (ModalConstraint) other;
39 return modality == otherModalConstraint.modality && constraint.equals(helper, otherModalConstraint.constraint); 49 return modality == otherModalConstraint.modality &&
50 concreteness == otherModalConstraint.concreteness &&
51 constraint.equals(helper, otherModalConstraint.constraint);
40 } 52 }
41 53
42 @Override 54 @Override
43 public String toReferenceString() { 55 public String toReferenceString() {
44 return FORMAT.formatted(modality, constraint.toReferenceString()); 56 return formatName(constraint.toReferenceString());
45 } 57 }
46 58
47 @Override 59 @Override
48 public String toString() { 60 public String toString() {
49 return FORMAT.formatted(modality, constraint); 61 return formatName(constraint.toString());
62 }
63
64 private String formatName(String constraintName) {
65 if (concreteness == Concreteness.PARTIAL) {
66 return "%s %s".formatted(modality, constraintName);
67 }
68 return "%s %s %s".formatted(modality, concreteness, constraintName);
69 }
70
71 public static Constraint of(Modality modality, Concreteness concreteness, Constraint constraint) {
72 if (constraint instanceof AnySymbolView || constraint instanceof ModalConstraint) {
73 // Symbol views and lifted constraints are already concrete. Thus, they cannot be abstracted at all.
74 return constraint;
75 }
76 return new ModalConstraint(modality, concreteness, constraint);
50 } 77 }
51} 78}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Modality.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Modality.java
index 96466d5c..c99a0399 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Modality.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/Modality.java
@@ -11,14 +11,12 @@ import java.util.Locale;
11 11
12public enum Modality { 12public enum Modality {
13 MUST, 13 MUST,
14 MAY, 14 MAY;
15 CURRENT;
16 15
17 public Modality negate() { 16 public Modality negate() {
18 return switch(this) { 17 return switch(this) {
19 case MUST -> MAY; 18 case MUST -> MAY;
20 case MAY -> MUST; 19 case MAY -> MUST;
21 case CURRENT -> CURRENT;
22 }; 20 };
23 } 21 }
24 22
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java
index 0e46a795..4f07f17d 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java
@@ -20,10 +20,6 @@ public final class PartialLiterals {
20 return addModality(literal, Modality.MUST); 20 return addModality(literal, Modality.MUST);
21 } 21 }
22 22
23 public static CallLiteral current(CallLiteral literal) {
24 return addModality(literal, Modality.CURRENT);
25 }
26
27 public static CallLiteral addModality(CallLiteral literal, Modality modality) { 23 public static CallLiteral addModality(CallLiteral literal, Modality modality) {
28 var target = literal.getTarget(); 24 var target = literal.getTarget();
29 if (target instanceof ModalConstraint) { 25 if (target instanceof ModalConstraint) {
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialSymbol.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialSymbol.java
index 3a08bdd8..2f04534a 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialSymbol.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialSymbol.java
@@ -14,4 +14,12 @@ public sealed interface PartialSymbol<A, C> extends AnyPartialSymbol permits Par
14 A defaultValue(); 14 A defaultValue();
15 15
16 C defaultConcreteValue(); 16 C defaultConcreteValue();
17
18 static PartialRelation of(String name, int arity) {
19 return new PartialRelation(name, arity);
20 }
21
22 static <A, C> PartialFunction<A, C> of(String name, int arity, AbstractDomain<A, C> abstractDomain) {
23 return new PartialFunction<>(name, arity, abstractDomain);
24 }
17} 25}