All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ai.libs.jaicore.planning.hierarchical.algorithms.forwarddecomposition.graphgenerators.rtn.RTNGraphGenerator Maven / Gradle / Ivy

package ai.libs.jaicore.planning.hierarchical.algorithms.forwarddecomposition.graphgenerators.rtn;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ai.libs.jaicore.basic.sets.SetUtil;
import ai.libs.jaicore.logic.fol.structure.ConstantParam;
import ai.libs.jaicore.logic.fol.structure.Literal;
import ai.libs.jaicore.logic.fol.structure.Monom;
import ai.libs.jaicore.planning.classical.algorithms.strips.forward.StripsUtil;
import ai.libs.jaicore.planning.classical.problems.ceoc.CEOCAction;
import ai.libs.jaicore.planning.classical.problems.ceoc.CEOCOperation;
import ai.libs.jaicore.planning.classical.problems.strips.Operation;
import ai.libs.jaicore.planning.core.Action;
import ai.libs.jaicore.planning.hierarchical.algorithms.forwarddecomposition.graphgenerators.TaskPlannerUtil;
import ai.libs.jaicore.planning.hierarchical.problems.rtn.RTNPlanningProblem;
import ai.libs.jaicore.planning.hierarchical.problems.stn.Method;
import ai.libs.jaicore.planning.hierarchical.problems.stn.MethodInstance;
import ai.libs.jaicore.search.core.interfaces.GraphGenerator;
import ai.libs.jaicore.search.model.travesaltree.NodeExpansionDescription;
import ai.libs.jaicore.search.structure.graphgenerator.NodeGoalTester;
import ai.libs.jaicore.search.structure.graphgenerator.SingleRootGenerator;
import ai.libs.jaicore.search.structure.graphgenerator.SuccessorGenerator;

public class RTNGraphGenerator implements GraphGenerator {

	private static final Logger logger = LoggerFactory.getLogger(RTNGraphGenerator.class);

	private final RTNPlanningProblem problem;
	private final Map primitiveTasks = new HashMap<>();
	private final TaskPlannerUtil util = new TaskPlannerUtil(null);

	public RTNGraphGenerator(final RTNPlanningProblem problem) {
		this.problem = problem;
		for (Operation op : problem.getDomain().getOperations()) {
			this.primitiveTasks.put(op.getName(), op);
		}
	}

	@Override
	public SingleRootGenerator getRootGenerator() {
		return () -> new RTNNode(false, this.problem.getInit(), new ArrayList<>(this.util.getTaskChainOfTotallyOrderedNetwork(this.problem.getNetwork())));
	}

	@Override
	public SuccessorGenerator getSuccessorGenerator() {
		return l -> {
			final List> successors = new ArrayList<>();
			final Monom state = l.getState();
			final List currentlyRemainingTasks = l.getRemainingTasks();
			final Literal nextTaskTmp = currentlyRemainingTasks.get(0);
			if (nextTaskTmp == null) {
				return successors;
			}
			final Literal nextTask = new Literal(nextTaskTmp.getPropertyName().substring(nextTaskTmp.getPropertyName().indexOf('-') + 1, nextTaskTmp.getPropertyName().length()),
					nextTaskTmp.getParameters());
			final String actualTaskName = nextTask.getPropertyName();

			/* if this is an or-node, perform the split as always */
			if (!l.isAndNode()) {

				/* if the task is primitive */

				if (this.primitiveTasks.containsKey(actualTaskName)) {

					logger.info("Computing successors for PRIMITIVE task {} in state {}", nextTask, state);

					final Collection applicableActions = this.util.getActionsForPrimitiveTaskThatAreApplicableInState(null, this.primitiveTasks.get(actualTaskName), nextTask,
							state);
					for (Action applicableAction : applicableActions) {
						logger.info("Adding successor for PRIMITIVE task {} in state {}: {}", nextTask, state, applicableAction.getEncoding());

						assert state.containsAll(applicableAction.getPrecondition().stream().filter(Literal::isPositive).collect(Collectors.toList()))
						&& SetUtil.disjoint(state, applicableAction.getPrecondition().stream().filter(Literal::isNegated).collect(Collectors.toList())) : ("Action "
								+ applicableAction + " is supposed to be aplpicable in state " + state + " but it is not!");

						/* if the depth is % k == 0, then compute the rest problem explicitly */
						final Monom updatedState = new Monom(state, false);
						final CEOCOperation op = (CEOCOperation) applicableAction.getOperation();
						final CEOCAction relevantAction = new CEOCAction(op, applicableAction.getGrounding());
						try {
							StripsUtil.updateState(updatedState, applicableAction);
						} catch (Exception e) {
							logger.error("apply {} to state: {}", applicableAction.getEncoding(), state);
							logger.error("addlists: {}", relevantAction.getAddLists());
							logger.error("Observed exception: {}", e);
						}
						final List remainingTasks = new ArrayList<>(currentlyRemainingTasks);
						remainingTasks.remove(0);
						boolean isAndNode = this.remainingTasksInitializeANDNode(remainingTasks);
						successors.add(new NodeExpansionDescription<>(l, new RTNNode(isAndNode, updatedState, remainingTasks), new RTNEdge(null, null, relevantAction), null));
					}
					assert checkDoubleNodes(successors);
					logger.info("Computed {} successors", successors.size());
				}

				/* otherwise determine methods for the task */
				else {

					logger.info("Computing successors for COMPLEX task {} in state {}", nextTask, state);
					final Set usedMethods = new HashSet<>();

					/* if this is an OR-Node */
					final Collection instances = this.util.getMethodInstancesForTaskThatAreApplicableInState(null, this.problem.getDomain().getMethods(), nextTask,
							state, currentlyRemainingTasks);
					for (MethodInstance instance : instances) {

						/* skip this instance if the method is lonely and we already used it */
						if (!usedMethods.contains(instance.getMethod())) {
							usedMethods.add(instance.getMethod());
						} else if (instance.getMethod().isLonely()) {
							continue;
						}

						assert state.containsAll(instance.getPrecondition().stream().filter(Literal::isPositive).collect(Collectors.toList()))
						&& SetUtil.disjoint(state, instance.getPrecondition().stream().filter(Literal::isNegated).collect(Collectors.toList())) : ("Instance "
								+ instance + " is supposed to be aplpicable in state " + state + " but it is not!");

						logger.info("Adding successor {}", instance);

						final List remainingTasks = new ArrayList<>(this.util.getTaskChainOfTotallyOrderedNetwork(instance.getNetwork()));

						final int indexForRemoval = remainingTasks.size();
						remainingTasks.addAll(currentlyRemainingTasks);
						remainingTasks.remove(indexForRemoval); // remove the first literal of the 2ndly appended list

						/* hard code the and-or-stuff for a moment */
						boolean isAndNode = this.remainingTasksInitializeANDNode(remainingTasks);
						successors.add(new NodeExpansionDescription<>(l, new RTNNode(isAndNode, state, remainingTasks), new RTNEdge(null, instance, null), null));
					}
				}
			}

			/* if this is an AND-node, create one successor for each refine action and the first after them */
			else {

				/* determine the next k tasks that are parallelizable */
				final List parallelizableTasks = new ArrayList<>();
				final List tasksForLastNode = new ArrayList<>(currentlyRemainingTasks);
				for (int i = 0; i < currentlyRemainingTasks.size(); i++) {
					Literal task = currentlyRemainingTasks.get(i);
					if (task.getPropertyName().contains("refine")) {
						parallelizableTasks.add(task);
						tasksForLastNode.remove(0);
					}
				}

				/* now create one successor for each of the refine-statements */
				for (Literal task : parallelizableTasks) {

					/* compute the reduced state */
					final Monom reducedState = new Monom();
					final Set relevantConstants = new HashSet<>(task.getConstantParams());
					for (String c : RTNUtil.getClassesThatExistInState(l)) {
						relevantConstants.add(new ConstantParam(c));
					}
					for (Literal lit : state) {
						if (relevantConstants.containsAll(lit.getConstantParams())) {
							reducedState.add(lit);
						}
					}

					/* rename clusters in reduced state */
					Map> clusters = new HashMap<>();
					for (Literal lit : reducedState) {
						if (lit.getPropertyName().equals("in")) {
							String item = lit.getConstantParams().get(0).getName();
							String cluster = lit.getConstantParams().get(1).getName();
							if (!clusters.containsKey(cluster)) {
								clusters.put(cluster, new ArrayList<>());
							}
							clusters.get(cluster).add(item);
						}
					}
					for (Entry> nameWithItems : clusters.entrySet()) {
						clusters.put(nameWithItems.getKey(), nameWithItems.getValue().stream().sorted().collect(Collectors.toList()));
					}
					final List toRemove = new ArrayList<>();
					final List toInsert = new ArrayList<>();
					for (Literal lit : reducedState) {
						if (lit.getPropertyName().equals("biggest")) {
							toRemove.add(lit);
							continue;
						} else if (!SetUtil.intersection(lit.getConstantParams().stream().map(ConstantParam::getName).collect(Collectors.toList()), clusters.keySet()).isEmpty()) {
							toRemove.add(lit);
							final List params = new ArrayList<>();
							for (ConstantParam p : lit.getConstantParams()) {
								params.add(clusters.containsKey(p.getName()) ? new ConstantParam(clusters.get(p.getName()).toString()) : p);
							}
							toInsert.add(new Literal(lit.getPropertyName(), params));
						}
					}
					reducedState.removeAll(toRemove);
					reducedState.addAll(toInsert);

					/* add the ground knowledge to the state (even about objects that are not there anymore) */
					for (Literal lit : state) {
						if (lit.getPropertyName().equals("bigger") && !reducedState.contains(lit)) {
							reducedState.add(lit);
						}
					}

					/* define the remaining task */
					final List remainingTask = new ArrayList<>();
					final List paramsForTask = new ArrayList<>();
					for (ConstantParam p : task.getConstantParams()) {
						paramsForTask.add(clusters.containsKey(p.getName()) ? new ConstantParam(clusters.get(p.getName()).toString()) : p);
					}

					/* define mapping for renaiming the subproblem solutions afterwards */
					Map mapping = new HashMap<>();
					for (Entry> nameWithItems : clusters.entrySet()) {
						mapping.put(new ConstantParam(nameWithItems.getValue().toString()), new ConstantParam(nameWithItems.getKey()));
					}

					remainingTask.add(new Literal(task.getPropertyName().substring(task.getPropertyName().indexOf('-') + 1), paramsForTask));
					successors.add(new NodeExpansionDescription<>(l, new RTNNode(false, reducedState, remainingTask), new RTNEdge(mapping, null, null), null));
				}

				/* now create one node for the remaining tasks */
				if (!tasksForLastNode.isEmpty()) {
					successors.add(new NodeExpansionDescription<>(l, new RTNNode(false, state, tasksForLastNode), new RTNEdge(null, null, null), null));
				}
			}

			logger.info("Computed {} successors", successors.size());
			return successors;
		};
	}

	@Override
	public NodeGoalTester getGoalTester() {
		return p -> p.getRemainingTasks().isEmpty();
	}

	private boolean remainingTasksInitializeANDNode(final List tasks) {
		if (tasks.isEmpty()) {
			return false;
		}
		Literal followingTask = tasks.get(0);
		return followingTask.getPropertyName().contains("refine");
	}

	private static boolean checkDoubleNodes(final List> successors) {
		if (successors.size() != new HashSet<>(successors).size()) {
			logger.error("Doppelte Knoten im Nachfolger!");
			return false;
		}
		return true;
	}

	@Override
	public boolean isSelfContained() {
		return false;
	}

	@Override
	public void setNodeNumbering(final boolean nodenumbering) {
		throw new UnsupportedOperationException();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy