Skip to content

Commit fa4d80e

Browse files
author
ctjoreilly
committed
Added
===== *. aima.search.online.LRTAStarAgent - Initial implementation of algorithm in Fig 4.23. *. aima.test.search.online.LRTAStarAgentTest - Test case for LRTAStarAgent - Note: One test currently commented out as algorithm goes into eternal loop based on the condition it is testing. *. aima.search.online.ActionState - Factored out code from OnlineDFSAgent for re-use in LRTAStarAgent Modified ======== *. aima.search.online.OnlineDFSAgent - Factored out ActionState for re-use in LRTAStarAgent. *. aima.test.learningtest.neural.BackPropagationTests - Commented out System.outs *. aim.test.search.SearchTests - Added LRTAStarAgentTest to suite.
1 parent 354e69d commit fa4d80e

File tree

6 files changed

+304
-34
lines changed

6 files changed

+304
-34
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package aima.search.online;
2+
3+
import aima.basic.Percept;
4+
5+
public class ActionState {
6+
private final Object action;
7+
private final Percept state;
8+
9+
public ActionState(Object action, Percept state) {
10+
this.action = action;
11+
this.state = state;
12+
}
13+
14+
public Object getAction() {
15+
return action;
16+
}
17+
18+
public Percept getState() {
19+
return state;
20+
}
21+
22+
@Override
23+
public boolean equals(Object o) {
24+
if (o == null || !(o instanceof ActionState)) {
25+
return super.equals(o);
26+
}
27+
return (action.equals(((ActionState) o).action) && state
28+
.equals(((ActionState) o).state));
29+
}
30+
31+
@Override
32+
public int hashCode() {
33+
return action.hashCode() + state.hashCode();
34+
}
35+
36+
@Override
37+
public String toString() {
38+
return "(" + action + ", " + state + ")";
39+
}
40+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package aima.search.online;
2+
3+
import java.util.ArrayList;
4+
import java.util.Hashtable;
5+
import java.util.List;
6+
7+
import aima.basic.Agent;
8+
import aima.basic.Percept;
9+
import aima.search.framework.Problem;
10+
import aima.search.framework.Successor;
11+
12+
/**
13+
* Artificial Intelligence A Modern Approach (2nd Edition): Figure 4.23, page
14+
* 128.<br>
15+
* <code>
16+
* function LRTA*AGENT(s') returns an action
17+
* inputs: s', a percept that identifies the current state
18+
* static: result, a table, indexed by action and state, initially empty
19+
* H, a table of cost estimates indexed by state, initially empty
20+
* s, a, the previous state and action, initially null
21+
*
22+
* if GOAL-TEST(s') then return stop
23+
* if s' is new state (not in H) then H[s'] <- h(s')
24+
* unless s is null
25+
* result[a, s] <- s'
26+
* H[s] <- min LRTA*-COST(s, b, result[b, s], H)
27+
* b (element of) ACTIONS(s)
28+
* a <- an action b in ACTIONS(s') that minimizes LRTA*-COST(s', b, result[b, s'], H)
29+
* s <- s'
30+
* return a
31+
*
32+
*
33+
* function LRTA*-COST(s, a, s', H) returns a cost estimate
34+
* if s' is undefined then return h(s)
35+
* else return c(s, a, s') + H[s']
36+
* </code>
37+
*
38+
* Figure 4.23 LRTA*-AGENT selects an action according to the value of
39+
* neighboring states, which are updated as the agent moves about the state
40+
* space.<br>
41+
* Note: This algorithm fails to exit if the goal does not exist (e.g. A<->B Goal=X),
42+
* this could be an issue with the implementation. Comments welcome.
43+
*/
44+
45+
/**
46+
* @author Ciaran O'Reilly
47+
*
48+
*/
49+
public class LRTAStarAgent extends Agent {
50+
51+
private Problem problem;
52+
// static: result, a table, indexed by action and state, initially empty
53+
private final Hashtable<ActionState, Percept> result = new Hashtable<ActionState, Percept>();
54+
// H, a table of cost estimates indexed by state, initially empty
55+
private final Hashtable<Percept, Double> H = new Hashtable<Percept, Double>();
56+
// s, a, the previous state and action, initially null
57+
private Percept s = null;
58+
private Object a = null;
59+
60+
public LRTAStarAgent(Problem problem) {
61+
setProblem(problem);
62+
}
63+
64+
public Problem getProblem() {
65+
return problem;
66+
}
67+
68+
public void setProblem(Problem problem) {
69+
this.problem = problem;
70+
init();
71+
}
72+
73+
// function LRTA*AGENT(s') returns an action
74+
// inputs: s', a percept that identifies the current state
75+
@Override
76+
public String execute(Percept sComma) {
77+
78+
// if GOAL-TEST(s') then return stop
79+
if (!goalTest(sComma)) {
80+
// if s' is new state (not in H) then H[s'] <- h(s')
81+
if (!H.containsKey(sComma)) {
82+
H.put(sComma, getProblem().getHeuristicFunction()
83+
.getHeuristicValue(sComma));
84+
}
85+
// unless s is null
86+
if (null != s) {
87+
// result[a, s] <- s'
88+
result.put(new ActionState(a, s), sComma);
89+
90+
// H[s] <- min LRTA*-COST(s, b, result[b, s], H)
91+
// b (element of) ACTIONS(s)
92+
double min = Double.MAX_VALUE;
93+
for (Object b : actions(s)) {
94+
double cost = lrtaCost(s, b, result.get(new ActionState(b,
95+
s)));
96+
if (cost < min) {
97+
min = cost;
98+
}
99+
}
100+
H.put(s, min);
101+
}
102+
// a <- an action b in ACTIONS(s') that minimizes LRTA*-COST(s', b,
103+
// result[b, s'], H)
104+
double min = Double.MAX_VALUE;
105+
// Just in case no actions
106+
a = Agent.NO_OP;
107+
for (Object b : actions(sComma)) {
108+
double cost = lrtaCost(sComma, b, result.get(new ActionState(b,
109+
sComma)));
110+
if (cost < min) {
111+
min = cost;
112+
a = b;
113+
}
114+
}
115+
} else {
116+
a = Agent.NO_OP;
117+
}
118+
119+
// s <- s'
120+
s = sComma;
121+
122+
if (Agent.NO_OP.equals(a)) {
123+
// I'm either at the Goal or can't get to it,
124+
// which in either case I'm finished so just die.
125+
die();
126+
}
127+
// return a
128+
return a.toString();
129+
}
130+
131+
//
132+
// PRIVATE METHODS
133+
//
134+
private void init() {
135+
live();
136+
result.clear();
137+
H.clear();
138+
s = null;
139+
a = null;
140+
}
141+
142+
private boolean goalTest(Percept state) {
143+
return getProblem().isGoalState(state);
144+
}
145+
146+
// function LRTA*-COST(s, a, s', H) returns a cost estimate
147+
private double lrtaCost(Percept s, Object action, Percept sComma) {
148+
// if s' is undefined then return h(s)
149+
if (null == sComma) {
150+
return getProblem().getHeuristicFunction().getHeuristicValue(s);
151+
}
152+
// else return c(s, a, s') + H[s']
153+
return getProblem().getStepCostFunction().calculateStepCost(s, sComma,
154+
action.toString())
155+
+ H.get(sComma);
156+
}
157+
158+
private List<Object> actions(Percept state) {
159+
List<Object> actions = new ArrayList<Object>();
160+
161+
List<Successor> successors = getProblem().getSuccessorFunction()
162+
.getSuccessors(state);
163+
164+
for (Successor s : successors) {
165+
actions.add(s.getAction());
166+
}
167+
168+
return actions;
169+
}
170+
}

src/aima/search/online/OnlineDFSAgent.java

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
* unexplored, a table that lists, for each visited state, the actions not yet tried
2020
* unbacktracked, a table that lists, for each visited state, the backtracks not yet tried
2121
* s, a, the previous state and action, initially null
22-
*
22+
*
2323
* if GOAL-TEST(s') then return stop
2424
* if s' is a new state then unexplored[s'] <- ACTIONS(s')
2525
* if s is not null then do
@@ -106,9 +106,9 @@ public String execute(Percept sComma) {
106106
Percept popped = unbacktracked.get(sComma).remove(
107107
unbacktracked.get(sComma).size() - 1);
108108
for (ActionState as : result.keySet()) {
109-
if (as.state.equals(sComma)
109+
if (as.getState().equals(sComma)
110110
&& result.get(as).equals(popped)) {
111-
a = as.action;
111+
a = as.getAction();
112112
break;
113113
}
114114
}
@@ -139,6 +139,7 @@ public String execute(Percept sComma) {
139139
//
140140

141141
private void init() {
142+
live();
142143
result.clear();
143144
unexplored.clear();
144145
unbacktracked.clear();
@@ -162,33 +163,4 @@ private List<Object> actions(Percept state) {
162163

163164
return actions;
164165
}
165-
166-
private class ActionState {
167-
private final Object action;
168-
private final Percept state;
169-
170-
public ActionState(Object action, Percept state) {
171-
this.action = action;
172-
this.state = state;
173-
}
174-
175-
@Override
176-
public boolean equals(Object o) {
177-
if (o == null || !(o instanceof ActionState)) {
178-
return super.equals(o);
179-
}
180-
return (action.equals(((ActionState) o).action) && state
181-
.equals(((ActionState) o).state));
182-
}
183-
184-
@Override
185-
public int hashCode() {
186-
return action.hashCode() + state.hashCode();
187-
}
188-
189-
@Override
190-
public String toString() {
191-
return "(" + action + ", " + state + ")";
192-
}
193-
}
194166
}

src/aima/test/learningtest/neural/BackPropagationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public void xtestDataSetPopulation() throws Exception {
137137

138138
innds.refreshDataset();
139139
int[] result = ffnn.testOnDataSet(innds);
140-
System.out.println(result[0] + " right, " + result[1] + " wrong");
140+
// System.out.println(result[0] + " right, " + result[1] + " wrong");
141141

142142
}
143143

@@ -154,7 +154,7 @@ public void testPerceptron() throws Exception {
154154

155155
innds.refreshDataset();
156156
int[] result = perc.testOnDataSet(innds);
157-
System.out.println(result[0] + " right, " + result[1] + " wrong");
157+
// System.out.println(result[0] + " right, " + result[1] + " wrong");
158158

159159
}
160160
}

src/aima/test/search/SearchTests.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import aima.test.search.nqueens.NQueensFitnessFunctionTest;
1818
import aima.test.search.nqueens.NQueensGoalTestTest;
1919
import aima.test.search.nqueens.NQueensSuccessorFunctionTest;
20+
import aima.test.search.online.LRTAStarAgentTest;
2021
import aima.test.search.online.OnlineDFSAgentTest;
2122
import aima.test.search.searches.AStarSearchTest;
2223
import aima.test.search.searches.BidirectionalSearchTest;
@@ -52,6 +53,7 @@ public static Test suite() {
5253
suite.addTest(new TestSuite(GreedyBestFirstSearchTest.class));
5354
suite.addTest(new TestSuite(IterativeDeepeningSearchTest.class));
5455
suite.addTest(new TestSuite(LIFOQueueTest.class));
56+
suite.addTest(new TestSuite(LRTAStarAgentTest.class));
5557
suite.addTest(new TestSuite(MapAgentTest.class));
5658
suite.addTest(new TestSuite(MapCSPTest.class));
5759
suite.addTest(new TestSuite(MapEnvironmentTest.class));
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package aima.test.search.online;
2+
3+
import junit.framework.TestCase;
4+
import aima.basic.BasicEnvironmentView;
5+
import aima.search.framework.HeuristicFunction;
6+
import aima.search.map.BidirectionalMapProblem;
7+
import aima.search.map.Map;
8+
import aima.search.map.MapEnvironment;
9+
import aima.search.online.LRTAStarAgent;
10+
11+
public class LRTAStarAgentTest extends TestCase {
12+
Map aMap;
13+
14+
StringBuffer envChanges;
15+
16+
HeuristicFunction hf;
17+
18+
@Override
19+
public void setUp() {
20+
aMap = new Map(new String[] { "A", "B", "C", "D", "E", "F" });
21+
aMap.addBidirectionalLink("A", "B", 4);
22+
aMap.addBidirectionalLink("B", "C", 4);
23+
aMap.addBidirectionalLink("C", "D", 4);
24+
aMap.addBidirectionalLink("D", "E", 4);
25+
aMap.addBidirectionalLink("E", "F", 4);
26+
hf = new HeuristicFunction() {
27+
public double getHeuristicValue(Object state) {
28+
return 1;
29+
}
30+
};
31+
32+
envChanges = new StringBuffer();
33+
}
34+
35+
public void testAlreadyAtGoal() {
36+
MapEnvironment me = new MapEnvironment(aMap);
37+
LRTAStarAgent agent = new LRTAStarAgent(new BidirectionalMapProblem(me
38+
.getMap(), "A", "A", hf));
39+
me.addAgent(agent, "A");
40+
me.registerView(new BasicEnvironmentView() {
41+
@Override
42+
public void envChanged(String command) {
43+
envChanges.append(command).append("->");
44+
}
45+
});
46+
me.stepUntilNoOp();
47+
48+
assertEquals("NoOP->", envChanges.toString());
49+
}
50+
51+
public void testNormalSearch() {
52+
MapEnvironment me = new MapEnvironment(aMap);
53+
LRTAStarAgent agent = new LRTAStarAgent(new BidirectionalMapProblem(me
54+
.getMap(), "A", "F", hf));
55+
me.addAgent(agent, "A");
56+
me.registerView(new BasicEnvironmentView() {
57+
@Override
58+
public void envChanged(String command) {
59+
envChanges.append(command).append("->");
60+
}
61+
});
62+
me.stepUntilNoOp();
63+
64+
assertEquals("B->A->B->C->B->C->D->C->D->E->D->E->F->NoOP->",
65+
envChanges.toString());
66+
}
67+
68+
public void testNoPath() {
69+
// Note: Will search forever if no path is possible.
70+
// MapEnvironment me = new MapEnvironment(aMap);
71+
// LRTAStarAgent agent = new LRTAStarAgent(new
72+
// BidirectionalMapProblem(me
73+
// .getMap(), "A", "G", hf));
74+
// me.addAgent(agent, "A");
75+
// me.registerView(new BasicEnvironmentView() {
76+
// @Override
77+
// public void envChanged(String command) {
78+
// envChanges.append(command).append("->");
79+
// }
80+
// });
81+
// me.stepUntilNoOp();
82+
//
83+
// assertEquals("B->A->B->C->B->C->D->C->D->E->D->E->F->NoOP->",
84+
// envChanges.toString());
85+
}
86+
}

0 commit comments

Comments
 (0)