aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java')
-rw-r--r--subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java132
1 files changed, 132 insertions, 0 deletions
diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java
new file mode 100644
index 00000000..d63ddfdd
--- /dev/null
+++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java
@@ -0,0 +1,132 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.dse.transition.actions;
7
8import org.eclipse.collections.api.factory.primitive.ObjectIntMaps;
9import org.eclipse.collections.api.map.primitive.MutableObjectIntMap;
10import org.jetbrains.annotations.Nullable;
11import tools.refinery.store.model.Model;
12import tools.refinery.store.query.dnf.RelationalQuery;
13import tools.refinery.store.query.dnf.SymbolicParameter;
14import tools.refinery.store.query.term.NodeVariable;
15
16import java.util.*;
17
18public class Action {
19 private final List<NodeVariable> parameters;
20 private final Set<NodeVariable> localVariables;
21 private final List<ActionLiteral> actionLiterals;
22 private final int[] @Nullable [] inputAllocations;
23 private final int[] @Nullable [] outputAllocations;
24
25 public Action(List<NodeVariable> parameters, List<? extends ActionLiteral> actionLiterals) {
26 this.parameters = List.copyOf(parameters);
27 this.actionLiterals = List.copyOf(actionLiterals);
28 var allocation = ObjectIntMaps.mutable.<NodeVariable>empty();
29 int arity = parameters.size();
30 for (int i = 0; i < arity; i++) {
31 allocation.put(parameters.get(i), i);
32 }
33 var mutableLocalVariables = new LinkedHashSet<NodeVariable>();
34 int size = actionLiterals.size();
35 inputAllocations = new int[size][];
36 outputAllocations = new int[size][];
37 for (int i = 0; i < size; i++) {
38 computeInputAllocation(i, parameters, allocation);
39 computeOutputAllocation(i, mutableLocalVariables, allocation);
40 }
41 this.localVariables = Collections.unmodifiableSet(mutableLocalVariables);
42 }
43
44 private void computeInputAllocation(int actionIndex, List<NodeVariable> parameters,
45 MutableObjectIntMap<NodeVariable> allocation) {
46 var actionLiteral = actionLiterals.get(actionIndex);
47 var inputVariables = actionLiteral.getInputVariables();
48 if (inputVariables.equals(parameters)) {
49 // Identity mappings use a {@code null} allocation to pass the activation tuple unchanged.
50 return;
51 }
52 var inputs = new int[inputVariables.size()];
53 for (int i = 0; i < inputs.length; i++) {
54 var variable = inputVariables.get(i);
55 if (!allocation.containsKey(variable)) {
56 throw new IllegalArgumentException("Unbound input variable %s of action literal %s"
57 .formatted(variable, actionLiteral));
58 }
59 inputs[i] = allocation.get(variable);
60 }
61 inputAllocations[actionIndex] = inputs;
62 }
63
64 private void computeOutputAllocation(int actionIndex, Set<NodeVariable> mutableLocalVariable,
65 MutableObjectIntMap<NodeVariable> allocation) {
66 var actionLiteral = actionLiterals.get(actionIndex);
67 var outputVariables = actionLiteral.getOutputVariables();
68 int size = outputVariables.size();
69 if (size == 0) {
70 // Identity mappings use a {@code null} allocation to avoid iterating over the output tuple.
71 return;
72 }
73 if (size >= 2 && new HashSet<>(outputVariables).size() != size) {
74 throw new IllegalArgumentException("Action literal %s has duplicate output variables %s"
75 .formatted(actionLiteral, outputVariables));
76 }
77 int arity = parameters.size();
78 var outputs = new int[size];
79 for (int i = 0; i < size; i++) {
80 var variable = outputVariables.get(i);
81 if (allocation.containsKey(variable)) {
82 throw new IllegalArgumentException("Output variable %s of action literal %s was already assigned"
83 .formatted(variable, actionLiteral));
84 }
85 int variableId = mutableLocalVariable.size();
86 allocation.put(variable, arity + variableId);
87 outputs[i] = variableId;
88 mutableLocalVariable.add(variable);
89 }
90 outputAllocations[actionIndex] = outputs;
91 }
92
93 public List<NodeVariable> getParameters() {
94 return parameters;
95 }
96
97 public int getArity() {
98 return parameters.size();
99 }
100
101 public Set<NodeVariable> getLocalVariables() {
102 return localVariables;
103 }
104
105 public List<ActionLiteral> getActionLiterals() {
106 return actionLiterals;
107 }
108
109 int @Nullable [] getInputAllocation(int actionIndex) {
110 return inputAllocations[actionIndex];
111 }
112
113 int @Nullable [] getOutputAllocation(int actionIndex) {
114 return outputAllocations[actionIndex];
115 }
116
117 public BoundAction bindToModel(Model model) {
118 return new BoundAction(this, model);
119 }
120
121 public static Action ofSymbolicParameters(List<SymbolicParameter> symbolicParameters,
122 List<? extends ActionLiteral> actionLiterals) {
123 var nodeVariables = symbolicParameters.stream()
124 .map(symbolicParameter -> symbolicParameter.getVariable().asNodeVariable())
125 .toList();
126 return new Action(nodeVariables, actionLiterals);
127 }
128
129 public static Action ofPrecondition(RelationalQuery precondition, List<? extends ActionLiteral> actionLiterals) {
130 return ofSymbolicParameters(precondition.getDnf().getSymbolicParameters(), actionLiterals);
131 }
132}