aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/actions/Action.java
blob: d63ddfdd17defa2f5807a555e133f885974c8f52 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package tools.refinery.store.dse.transition.actions;

import org.eclipse.collections.api.factory.primitive.ObjectIntMaps;
import org.eclipse.collections.api.map.primitive.MutableObjectIntMap;
import org.jetbrains.annotations.Nullable;
import tools.refinery.store.model.Model;
import tools.refinery.store.query.dnf.RelationalQuery;
import tools.refinery.store.query.dnf.SymbolicParameter;
import tools.refinery.store.query.term.NodeVariable;

import java.util.*;

public class Action {
	private final List<NodeVariable> parameters;
	private final Set<NodeVariable> localVariables;
	private final List<ActionLiteral> actionLiterals;
	private final int[] @Nullable [] inputAllocations;
	private final int[] @Nullable [] outputAllocations;

	public Action(List<NodeVariable> parameters, List<? extends ActionLiteral> actionLiterals) {
		this.parameters = List.copyOf(parameters);
		this.actionLiterals = List.copyOf(actionLiterals);
		var allocation = ObjectIntMaps.mutable.<NodeVariable>empty();
		int arity = parameters.size();
		for (int i = 0; i < arity; i++) {
			allocation.put(parameters.get(i), i);
		}
		var mutableLocalVariables = new LinkedHashSet<NodeVariable>();
		int size = actionLiterals.size();
		inputAllocations = new int[size][];
		outputAllocations = new int[size][];
		for (int i = 0; i < size; i++) {
			computeInputAllocation(i, parameters, allocation);
			computeOutputAllocation(i, mutableLocalVariables, allocation);
		}
		this.localVariables = Collections.unmodifiableSet(mutableLocalVariables);
	}

	private void computeInputAllocation(int actionIndex, List<NodeVariable> parameters,
										MutableObjectIntMap<NodeVariable> allocation) {
		var actionLiteral = actionLiterals.get(actionIndex);
		var inputVariables = actionLiteral.getInputVariables();
		if (inputVariables.equals(parameters)) {
			// Identity mappings use a {@code null} allocation to pass the activation tuple unchanged.
			return;
		}
		var inputs = new int[inputVariables.size()];
		for (int i = 0; i < inputs.length; i++) {
			var variable = inputVariables.get(i);
			if (!allocation.containsKey(variable)) {
				throw new IllegalArgumentException("Unbound input variable %s of action literal %s"
						.formatted(variable, actionLiteral));
			}
			inputs[i] = allocation.get(variable);
		}
		inputAllocations[actionIndex] = inputs;
	}

	private void computeOutputAllocation(int actionIndex, Set<NodeVariable> mutableLocalVariable,
										 MutableObjectIntMap<NodeVariable> allocation) {
		var actionLiteral = actionLiterals.get(actionIndex);
		var outputVariables = actionLiteral.getOutputVariables();
		int size = outputVariables.size();
		if (size == 0) {
			// Identity mappings use a {@code null} allocation to avoid iterating over the output tuple.
			return;
		}
		if (size >= 2 && new HashSet<>(outputVariables).size() != size) {
			throw new IllegalArgumentException("Action literal %s has duplicate output variables %s"
					.formatted(actionLiteral, outputVariables));
		}
		int arity = parameters.size();
		var outputs = new int[size];
		for (int i = 0; i < size; i++) {
			var variable = outputVariables.get(i);
			if (allocation.containsKey(variable)) {
				throw new IllegalArgumentException("Output variable %s of action literal %s was already assigned"
						.formatted(variable, actionLiteral));
			}
			int variableId = mutableLocalVariable.size();
			allocation.put(variable, arity + variableId);
			outputs[i] = variableId;
			mutableLocalVariable.add(variable);
		}
		outputAllocations[actionIndex] = outputs;
	}

	public List<NodeVariable> getParameters() {
		return parameters;
	}

	public int getArity() {
		return parameters.size();
	}

	public Set<NodeVariable> getLocalVariables() {
		return localVariables;
	}

	public List<ActionLiteral> getActionLiterals() {
		return actionLiterals;
	}

	int @Nullable [] getInputAllocation(int actionIndex) {
		return inputAllocations[actionIndex];
	}

	int @Nullable [] getOutputAllocation(int actionIndex) {
		return outputAllocations[actionIndex];
	}

	public BoundAction bindToModel(Model model) {
		return new BoundAction(this, model);
	}

	public static Action ofSymbolicParameters(List<SymbolicParameter> symbolicParameters,
											  List<? extends ActionLiteral> actionLiterals) {
		var nodeVariables = symbolicParameters.stream()
				.map(symbolicParameter -> symbolicParameter.getVariable().asNodeVariable())
				.toList();
		return new Action(nodeVariables, actionLiterals);
	}

	public static Action ofPrecondition(RelationalQuery precondition, List<? extends ActionLiteral> actionLiterals) {
		return ofSymbolicParameters(precondition.getDnf().getSymbolicParameters(), actionLiterals);
	}
}