001 package dk.deepthought.sidious.planner;
002
003 import org.apache.commons.logging.Log;
004 import org.apache.commons.logging.LogFactory;
005
006 import dk.deepthought.sidious.greenhouse.ClimaticState;
007 import dk.deepthought.sidious.greenhouse.SensorInput;
008 import dk.deepthought.sidious.supportsystem.State;
009 import dk.deepthought.sidious.supportsystem.SuperLinkID;
010
011 /**
012 * The class encapsulates the heuristic function of the A*-algorithm.
013 * <p>
014 * The heuristic is calculated by evaluating all rules associated with the
015 * requester of the plan. In this way the estimated cost of getting to the goal
016 * is very close to the actual distance.
017 *
018 * @author Deepthought
019 *
020 */
021 public final class GreenhouseHeuristic implements Heuristic {
022 /**
023 * Logger for this class
024 */
025 private static final Log logger = LogFactory
026 .getLog(GreenhouseHeuristic.class);
027
028 /**
029 * The id of the requester.
030 */
031 private final SuperLinkID id;
032
033 /**
034 * The goal of the search.
035 */
036 private ClimaticState goal;
037
038 /**
039 * The source of the search.
040 */
041 private ClimaticState source;
042
043 /**
044 * The base euclidian distance. <code>distance(source, goal)</code>.
045 */
046 private double baseEuclidianDistance = Double.NaN;
047
048 /**
049 * Constructs a new <code>GreenhouseHeuristic</code> with the specified
050 * <code>id</code>.
051 *
052 * @param id
053 * the id to the requester.
054 * @param source
055 * the source state of any path in the graph
056 * @param goal
057 * the goal state of any path in the graph
058 */
059 public GreenhouseHeuristic(final SuperLinkID id, State source, State goal) {
060 if (id == null || source == null || goal == null) {
061 logger.error("GreenhouseHeuristic(SuperLinkID id=" + id
062 + ", State source=" + source + ", State goal=" + goal
063 + ") - not valid input");
064 throw new IllegalArgumentException("null not valid input");
065 }
066 this.id = id;
067 if (source instanceof ClimaticState) {
068 this.source = (ClimaticState) source;
069 }
070 if (goal instanceof ClimaticState) {
071 this.goal = (ClimaticState) goal;
072 }
073 }
074
075 /**
076 * This heuristic is based upon a normalized Euclidian distance.
077 * <p>
078 * This distance is the distance from the current vertex to the goal divided
079 * by the distance from source to goal.
080 *
081 * @see dk.deepthought.sidious.planner.Heuristic#h(dk.deepthought.sidious.supportsystem.State)
082 */
083 public double h(final State state) {
084 if (logger.isDebugEnabled()) {
085 logger.debug("heuristic(State state=" + state + ") - start");
086 }
087 if (state == null) {
088 String fail = "h(State state=null) - not valid state";
089 logger.error(fail);
090 throw new IllegalArgumentException(fail);
091 }
092 double returndouble = 0;
093 if (state instanceof ClimaticState) {
094 ClimaticState clima = (ClimaticState) state;
095
096 double normalizedEuclidian = normalizedEuclidian(clima);
097 returndouble = normalizedEuclidian;
098 }
099 if (logger.isDebugEnabled()) {
100 logger.debug("heuristic(State state=" + state
101 + ") - end - return value=" + returndouble);
102 }
103 return returndouble;
104 }
105
106 /**
107 * Calculates the normalized euclidian cost of getting form any state to the
108 * goal state.
109 *
110 * <pre>
111 * distance(any, goal) / distance(source, goal)
112 * </pre>
113 *
114 * @param state
115 * the state to calculate the distance from
116 *
117 * @return the normalized euclidian cost
118 */
119 private double normalizedEuclidian(ClimaticState state) {
120 if (Double.isNaN(baseEuclidianDistance)) {
121 baseEuclidianDistance = euclidianDistanceToGoal(source);
122 }
123 double normalizedDistance = euclidianDistanceToGoal(state)
124 / baseEuclidianDistance;
125 return normalizedDistance;
126 }
127
128 /**
129 * Calculates the euclidian distance to the goal state.
130 *
131 * @param state
132 * the source state
133 * @return the euclidian distance
134 */
135 double euclidianDistanceToGoal(ClimaticState state) {
136 if (state.partiallyEquals(goal)) {
137 return 0;
138 }
139 double total = 0;
140 for (SensorInput sensorFromGoal : goal.getSensors()) {
141 double sensorValue = sensorFromGoal.getValue();
142 for (SensorInput sensorFromState : state.getSensors()) {
143 if (sensorFromGoal.equalsOnSuperLinkID(sensorFromState)) {
144 sensorValue -= sensorFromState.getValue();
145 total += (sensorValue * sensorValue);
146 }
147 }
148
149 }
150 return Math.sqrt(total);
151 }
152
153 }
|