org.gerryai.htn.simple.constraint.validation.impl.SimpleConstraintValidator.java Source code

Java tutorial

Introduction

Here is the source code for org.gerryai.htn.simple.constraint.validation.impl.SimpleConstraintValidator.java

Source

/**
 *  Gerry AI - Open framework for automated planning algorithms
 *  Copyright (C) 2012  David Edwards
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.gerryai.htn.simple.constraint.validation.impl;

import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.gerryai.htn.constraint.AfterConstraint;
import org.gerryai.htn.constraint.BeforeConstraint;
import org.gerryai.htn.constraint.BetweenConstraint;
import org.gerryai.htn.constraint.Constraint;
import org.gerryai.htn.constraint.PrecedenceConstraint;
import org.gerryai.htn.simple.constraint.validation.ConstraintValidator;
import org.gerryai.htn.tasknetwork.InvalidConstraint;
import org.gerryai.htn.tasknetwork.Task;
import org.gerryai.htn.tasknetwork.TaskNetwork;
import org.gerryai.logic.Term;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

/**
 * Class for validating tasks and constraints.
 * @author David Edwards <david@more.fool.me.uk>
 */
public class SimpleConstraintValidator implements ConstraintValidator {

    /**
     * Set of tasks that have been added to the validator so far.
     */
    private Set<Task> tasks;

    /**
     * Set of precedence constraints that have been added so far.
     */
    private Set<PrecedenceConstraint> precedenceConstraints;

    /**
     * Set of before constraints that have been added so far.
     */
    private Set<BeforeConstraint> beforeConstraints;

    /**
     * Set of after constraints that have been added so far.
     */
    private Set<AfterConstraint> afterConstraints;

    /**
     * Set of between constraints that have been added so far.
     */
    private Set<BetweenConstraint> betweenConstraints;

    /**
     * Map of precedence constraints, using their preceding task as the key.
     * Used to simplify looking for cycles.
     */
    private Multimap<Task, PrecedenceConstraint> precedingTasks;

    @Override
    public final Set<Task> getTasks() {
        return Collections.unmodifiableSet(tasks);
    }

    @Override
    public final Set<BeforeConstraint> getBeforeConstraints() {
        return Collections.unmodifiableSet(beforeConstraints);
    }

    @Override
    public final Set<AfterConstraint> getAfterConstraints() {
        return Collections.unmodifiableSet(afterConstraints);
    }

    @Override
    public final Set<BetweenConstraint> getBetweenConstraints() {
        return Collections.unmodifiableSet(betweenConstraints);
    }

    @Override
    public final Set<PrecedenceConstraint> getPrecedenceConstraints() {
        return Collections.unmodifiableSet(precedenceConstraints);
    }

    /**
     * Default constructor.
     */
    public SimpleConstraintValidator() {
        tasks = new HashSet<Task>();
        precedenceConstraints = new HashSet<PrecedenceConstraint>();
        precedingTasks = HashMultimap.create();
        beforeConstraints = new HashSet<BeforeConstraint>();
        afterConstraints = new HashSet<AfterConstraint>();
        betweenConstraints = new HashSet<BetweenConstraint>();
    }

    @Override
    public final void add(PrecedenceConstraint constraint) throws InvalidConstraint {
        if (validate(constraint)) {
            precedenceConstraints.add(constraint);
            for (Task task : constraint.getPrecedingTasks()) {
                precedingTasks.get(task).add(constraint);
            }
        } else {
            throw new InvalidConstraint();
        }
    }

    @Override
    public final void add(BeforeConstraint constraint) throws InvalidConstraint {
        if (validate(constraint)) {
            beforeConstraints.add(constraint);
        } else {
            throw new InvalidConstraint();
        }
    }

    @Override
    public final void add(AfterConstraint constraint) throws InvalidConstraint {
        if (validate(constraint)) {
            afterConstraints.add(constraint);
        } else {
            throw new InvalidConstraint();
        }
    }

    @Override
    public final void add(BetweenConstraint constraint) throws InvalidConstraint {
        if (validate(constraint)) {
            betweenConstraints.add(constraint);
        } else {
            throw new InvalidConstraint();
        }
    }

    @Override
    public final void add(Task task) {
        tasks.add(task);
    }

    @Override
    public final void apply(Map<Term, Term> substitution) {

        // Build a map of tasks to their replacements
        Multimap<Task, Task> taskReplacementMap = HashMultimap.create();
        for (Task task : tasks) {
            Task newTask = task.applyToCopy(substitution);
            taskReplacementMap.put(task, newTask);
        }
        // Replace the existing tasks in this builder
        tasks = new HashSet<Task>(taskReplacementMap.values());

        // Build the sets of replacement constraints
        beforeConstraints = applyReplace(beforeConstraints, substitution, taskReplacementMap);
        afterConstraints = applyReplace(afterConstraints, substitution, taskReplacementMap);
        betweenConstraints = applyReplace(betweenConstraints, substitution, taskReplacementMap);
        precedenceConstraints = applyReplace(precedenceConstraints, substitution, taskReplacementMap);

    }

    @Override
    public final void replace(Task oldTask, TaskNetwork newTaskNetwork) {
        //TODO: Check implementation

        // Build a new set of tasks
        int numTasks = tasks.size() + newTaskNetwork.getTasks().size() - 1;
        Set<Task> newTasks = new HashSet<Task>(numTasks);
        for (Task task : tasks) {
            if (!task.equals(oldTask)) {
                newTasks.add(task);
            } else {
                newTasks.addAll(newTaskNetwork.getTasks());
            }
        }
        tasks = newTasks;

        // Update existing constraints
        Multimap<Task, Task> taskMap = HashMultimap.create();
        taskMap.putAll(oldTask, newTaskNetwork.getTasks());
        beforeConstraints = mergeAndReplace(beforeConstraints, newTaskNetwork.getBeforeConstraints(), taskMap);
        afterConstraints = mergeAndReplace(afterConstraints, newTaskNetwork.getAfterConstraints(), taskMap);
        betweenConstraints = mergeAndReplace(betweenConstraints, newTaskNetwork.getBetweenConstraints(), taskMap);
        precedenceConstraints = mergeAndReplace(precedenceConstraints, newTaskNetwork.getPrecedenceConstraints(),
                taskMap);
    }

    /**
     * Apply a task substitution to a set of constraints.
     * @param oldConstraints existing constraints
     * @param substitution the substitution to apply to the constraints
     * @param taskMap map of tasks to the sets of tasks to replace them with
     * @param <T> type of constraint being processed
     * @return the set of constraints with the task substitution applied
     */
    private <T extends Constraint<T>> Set<T> applyReplace(Set<T> oldConstraints, Map<Term, Term> substitution,
            Multimap<Task, Task> taskMap) {
        Set<T> newConstraints = new HashSet<T>(oldConstraints.size());
        for (T constraint : oldConstraints) {
            T newConstraint = constraint.apply(substitution).replace(taskMap);
            newConstraints.add(newConstraint);
        }
        return newConstraints;
    }

    /**
     * Combine two sets of constraints and apply a task substitution to the old constraints.
     * @param oldConstraints existing constraints
     * @param newConstraints new constraints to add
     * @param taskMap map of tasks to the sets of tasks to replace them with
     * @param <T> type of constraint being processed
     * @return the combined set of constraints
     */
    private <T extends Constraint<T>> Set<T> mergeAndReplace(Set<T> oldConstraints, Set<T> newConstraints,
            Multimap<Task, Task> taskMap) {
        int numConstraints = oldConstraints.size() + newConstraints.size();
        Set<T> updatedConstraints = new HashSet<T>(numConstraints);
        for (T oldConstraint : oldConstraints) {
            T newConstraint = oldConstraint.replace(taskMap);
            updatedConstraints.add(newConstraint);
        }
        // Add the new constraints
        updatedConstraints.addAll(newConstraints);
        return updatedConstraints;
    }

    /**
     * Validation check for simple precedence constraints, but does not add the constraint.
     * @param constraint the constraint to validate
     * @return true if the constraint passes validation
     */
    public final boolean validate(PrecedenceConstraint constraint) {

        // Check tasks have already been added
        for (Task task : constraint.getPrecedingTasks()) {
            if (!tasks.contains(task)) {
                return false;
            }
        }
        for (Task task : constraint.getProcedingTasks()) {
            if (!tasks.contains(task)) {
                return false;
            }
        }

        // Check if an existing identical constraint exists
        for (PrecedenceConstraint existingConstraint : precedenceConstraints) {
            if (existingConstraint.equals(constraint)) {
                return false;
            }
        }

        //Check for cycles
        for (Task precedingTask : constraint.getPrecedingTasks()) {
            for (Task procedingTask : constraint.getProcedingTasks()) {
                if (checkForPrecedenceConstraintCycle(precedingTask, procedingTask)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Validation check for simple before constraints, but does not add the constraint.
     * @param constraint the constraint to validate
     * @return true if the constraint passes validation
     */
    private boolean validate(BeforeConstraint constraint) {
        // Check tasks have already been added
        for (Task task : constraint.getTasks()) {
            if (!tasks.contains(task)) {
                return false;
            }
        }
        // Check if an existing identical constraint exists
        for (BeforeConstraint existingConstraint : beforeConstraints) {
            if (existingConstraint.equals(constraint)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Validation check for simple after constraints, but does not add the constraint.
     * @param constraint the constraint to validate
     * @return true if the constraint passes validation
     */
    private boolean validate(AfterConstraint constraint) {
        // Check tasks have already been added
        for (Task task : constraint.getTasks()) {
            if (!tasks.contains(task)) {
                return false;
            }
        }
        // Check if an existing identical constraint exists
        for (AfterConstraint existingConstraint : afterConstraints) {
            if (existingConstraint.equals(constraint)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Validation check for simple between constraints, but does not add the constraint.
     * @param constraint the constraint to validate
     * @return true if the constraint passes validation
     */
    private boolean validate(BetweenConstraint constraint) {
        // Check tasks have already been added
        for (Task task : constraint.getPrecedingTasks()) {
            if (!tasks.contains(task)) {
                return false;
            }
        }
        for (Task task : constraint.getProcedingTasks()) {
            if (!tasks.contains(task)) {
                return false;
            }
        }
        // Check if an existing identical constraint exists
        for (BetweenConstraint existingConstraint : betweenConstraints) {
            if (existingConstraint.equals(constraint)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Helper method to help check for cycles.
     * @param initialTask task we are looking for
     * @param nextTask next task we should check from
     * @return true if a cycle was found
     */
    private boolean checkForPrecedenceConstraintCycle(Task initialTask, Task nextTask) {

        if (initialTask.equals(nextTask)) {
            return true;
        } else if (!precedingTasks.containsKey(nextTask)) {
            return false;
        } else {
            for (PrecedenceConstraint constraint : precedingTasks.get(nextTask)) {
                for (Task task : constraint.getProcedingTasks()) {
                    if (checkForPrecedenceConstraintCycle(initialTask, task)) {
                        return true;
                    }
                }
            }
            return false;
        }

    }
}