Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aimacode
GitHub Repository: aimacode/aima-python
Path: blob/master/making_simple_decision4e.py
615 views
1
"""Making Simple Decisions (Chapter 15)"""
2
3
import random
4
5
from agents import Agent
6
from probability import BayesNet
7
from utils4e import vector_add, weighted_sample_with_replacement
8
9
10
class DecisionNetwork(BayesNet):
11
"""An abstract class for a decision network as a wrapper for a BayesNet.
12
Represents an agent's current state, its possible actions, reachable states
13
and utilities of those states."""
14
15
def __init__(self, action, infer):
16
"""action: a single action node
17
infer: the preferred method to carry out inference on the given BayesNet"""
18
super().__init__()
19
self.action = action
20
self.infer = infer
21
22
def best_action(self):
23
"""Return the best action in the network"""
24
return self.action
25
26
def get_utility(self, action, state):
27
"""Return the utility for a particular action and state in the network"""
28
raise NotImplementedError
29
30
def get_expected_utility(self, action, evidence):
31
"""Compute the expected utility given an action and evidence"""
32
u = 0.0
33
prob_dist = self.infer(action, evidence, self).prob
34
for item, _ in prob_dist.items():
35
u += prob_dist[item] * self.get_utility(action, item)
36
37
return u
38
39
40
class InformationGatheringAgent(Agent):
41
"""A simple information gathering agent. The agent works by repeatedly selecting
42
the observation with the highest information value, until the cost of the next
43
observation is greater than its expected benefit. [Figure 16.9]"""
44
45
def __init__(self, decnet, infer, initial_evidence=None):
46
"""decnet: a decision network
47
infer: the preferred method to carry out inference on the given decision network
48
initial_evidence: initial evidence"""
49
super().__init__()
50
self.decnet = decnet
51
self.infer = infer
52
self.observation = initial_evidence or []
53
self.variables = self.decnet.nodes
54
55
def integrate_percept(self, percept):
56
"""Integrate the given percept into the decision network"""
57
raise NotImplementedError
58
59
def execute(self, percept):
60
"""Execute the information gathering algorithm"""
61
self.observation = self.integrate_percept(percept)
62
vpis = self.vpi_cost_ratio(self.variables)
63
j = max(vpis)
64
variable = self.variables[j]
65
66
if self.vpi(variable) > self.cost(variable):
67
return self.request(variable)
68
69
return self.decnet.best_action()
70
71
def request(self, variable):
72
"""Return the value of the given random variable as the next percept"""
73
raise NotImplementedError
74
75
def cost(self, var):
76
"""Return the cost of obtaining evidence through tests, consultants or questions"""
77
raise NotImplementedError
78
79
def vpi_cost_ratio(self, variables):
80
"""Return the VPI to cost ratio for the given variables"""
81
v_by_c = []
82
for var in variables:
83
v_by_c.append(self.vpi(var) / self.cost(var))
84
return v_by_c
85
86
def vpi(self, variable):
87
"""Return VPI for a given variable"""
88
vpi = 0.0
89
prob_dist = self.infer(variable, self.observation, self.decnet).prob
90
for item, _ in prob_dist.items():
91
post_prob = prob_dist[item]
92
new_observation = list(self.observation)
93
new_observation.append(item)
94
expected_utility = self.decnet.get_expected_utility(variable, new_observation)
95
vpi += post_prob * expected_utility
96
97
vpi -= self.decnet.get_expected_utility(variable, self.observation)
98
return vpi
99
100
101
# _________________________________________________________________________
102
# chapter 25 Robotics
103
# TODO: Implement continuous map for MonteCarlo similar to Fig25.10 from the book
104
105
106
class MCLmap:
107
"""Map which provides probability distributions and sensor readings.
108
Consists of discrete cells which are either an obstacle or empty"""
109
110
def __init__(self, m):
111
self.m = m
112
self.nrows = len(m)
113
self.ncols = len(m[0])
114
# list of empty spaces in the map
115
self.empty = [(i, j) for i in range(self.nrows) for j in range(self.ncols) if not m[i][j]]
116
117
def sample(self):
118
"""Returns a random kinematic state possible in the map"""
119
pos = random.choice(self.empty)
120
# 0N 1E 2S 3W
121
orient = random.choice(range(4))
122
kin_state = pos + (orient,)
123
return kin_state
124
125
def ray_cast(self, sensor_num, kin_state):
126
"""Returns distace to nearest obstacle or map boundary in the direction of sensor"""
127
pos = kin_state[:2]
128
orient = kin_state[2]
129
# sensor layout when orientation is 0 (towards North)
130
# 0
131
# 3R1
132
# 2
133
delta = ((sensor_num % 2 == 0) * (sensor_num - 1), (sensor_num % 2 == 1) * (2 - sensor_num))
134
# sensor direction changes based on orientation
135
for _ in range(orient):
136
delta = (delta[1], -delta[0])
137
range_count = 0
138
while (0 <= pos[0] < self.nrows) and (0 <= pos[1] < self.nrows) and (not self.m[pos[0]][pos[1]]):
139
pos = vector_add(pos, delta)
140
range_count += 1
141
return range_count
142
143
144
def monte_carlo_localization(a, z, N, P_motion_sample, P_sensor, m, S=None):
145
"""Monte Carlo localization algorithm from Fig 25.9"""
146
147
def ray_cast(sensor_num, kin_state, m):
148
return m.ray_cast(sensor_num, kin_state)
149
150
M = len(z)
151
W = [0] * N
152
S_ = [0] * N
153
W_ = [0] * N
154
v = a['v']
155
w = a['w']
156
157
if S is None:
158
S = [m.sample() for _ in range(N)]
159
160
for i in range(N):
161
S_[i] = P_motion_sample(S[i], v, w)
162
W_[i] = 1
163
for j in range(M):
164
z_ = ray_cast(j, S_[i], m)
165
W_[i] = W_[i] * P_sensor(z[j], z_)
166
167
S = weighted_sample_with_replacement(N, S_, W_)
168
return S
169
170