Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,7 @@ def parse_definite_clause(s):


# Useful constant Exprs used in examples and code:
A, B, C, D, E, F, G, P, Q, x, y, z = map(Expr, 'ABCDEFGPQxyz')

A, B, C, D, E, F, G, P, Q, a, x, y, z, u = map(Expr, 'ABCDEFGPQaxyzu')

# ______________________________________________________________________________

Expand Down Expand Up @@ -1370,7 +1369,9 @@ def unify_var(var, x, s):
elif occur_check(var, x, s):
return None
else:
return extend(s, var, x)
new_s = extend(s, var, x)
cascade_substitution(new_s)
return new_s


def occur_check(var, x, s):
Expand Down Expand Up @@ -1415,6 +1416,33 @@ def subst(s, x):
else:
return Expr(x.op, *[subst(s, arg) for arg in x.args])

def cascade_substitution(s):
"""This method allows to return a correct unifier in normal form
and perform a cascade substitution to s.
For every mapping in s perform a cascade substitution on s.get(x)
and if it is replaced with a function ensure that all the function
terms are correct updates by passing over them again.

This issue fix: https://github.com/aimacode/aima-python/issues/1053
unify(expr('P(A, x, F(G(y)))'), expr('P(z, F(z), F(u))'))
must return {z: A, x: F(A), u: G(y)} and not {z: A, x: F(z), u: G(y)}

>>> s = {x: y, y: G(z)}
>>> cascade_substitution(s)
>>> print(s)
{x: G(z), y: G(z)}

Parameters
----------
s : Dictionary
This contain a substution
"""

for x in s:
s[x] = subst(s, s.get(x))
if isinstance(s.get(x), Expr) and not is_variable(s.get(x)):
# Ensure Function Terms are correct updates by passing over them again.
s[x] = subst(s, s.get(x))

def standardize_variables(sentence, dic=None):
"""Replace all the variables in sentence with new variables."""
Expand Down
56 changes: 50 additions & 6 deletions probability.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,23 @@
from collections import defaultdict
from functools import reduce


# ______________________________________________________________________________


def DTAgentProgram(belief_state):
"""A decision-theoretic agent. [Figure 13.1]"""

def program(percept):
belief_state.observe(program.action, percept)
program.action = argmax(belief_state.actions(),
key=belief_state.expected_outcome_utility)
return program.action

program.action = None
return program


# ______________________________________________________________________________


Expand Down Expand Up @@ -132,6 +136,7 @@ def event_values(event, variables):
else:
return tuple([event[var] for var in variables])


# ______________________________________________________________________________


Expand Down Expand Up @@ -160,6 +165,7 @@ def enumerate_joint(variables, e, P):
return sum([enumerate_joint(rest, extend(e, Y, y), P)
for y in P.values(Y)])


# ______________________________________________________________________________


Expand Down Expand Up @@ -378,6 +384,7 @@ def __repr__(self):
('MaryCalls', 'Alarm', {T: 0.70, F: 0.01})
])


# ______________________________________________________________________________


Expand Down Expand Up @@ -409,6 +416,7 @@ def enumerate_all(variables, e, bn):
return sum(Ynode.p(y, e) * enumerate_all(rest, extend(e, Y, y), bn)
for y in bn.variable_values(Y))


# ______________________________________________________________________________


Expand Down Expand Up @@ -498,6 +506,7 @@ def all_events(variables, bn, e):
for x in bn.variable_values(X):
yield extend(e1, X, x)


# ______________________________________________________________________________

# [Figure 14.12a]: sprinkler network
Expand All @@ -510,6 +519,7 @@ def all_events(variables, bn, e):
('WetGrass', 'Sprinkler Rain',
{(T, T): 0.99, (T, F): 0.90, (F, T): 0.90, (F, F): 0.00})])


# ______________________________________________________________________________


Expand All @@ -521,6 +531,7 @@ def prior_sample(bn):
event[node.variable] = node.sample(event)
return event


# _________________________________________________________________________


Expand All @@ -547,6 +558,7 @@ def consistent_with(event, evidence):
return all(evidence.get(k, v) == v
for k, v in event.items())


# _________________________________________________________________________


Expand Down Expand Up @@ -579,6 +591,7 @@ def weighted_sample(bn, e):
event[Xi] = node.sample(event)
return event, w


# _________________________________________________________________________


Expand Down Expand Up @@ -612,6 +625,7 @@ def markov_blanket_sample(X, e, bn):
# (assuming a Boolean variable here)
return probability(Q.normalize()[True])


# _________________________________________________________________________


Expand Down Expand Up @@ -655,7 +669,7 @@ def forward_backward(HMM, ev, prior):

fv = [[0.0, 0.0] for _ in range(len(ev))]
b = [1.0, 1.0]
bv = [b] # we don't need bv; but we will have a list of all backward messages here
bv = [b] # we don't need bv; but we will have a list of all backward messages here
sv = [[0, 0] for _ in range(len(ev))]

fv[0] = prior
Expand All @@ -671,6 +685,33 @@ def forward_backward(HMM, ev, prior):

return sv


def viterbi(HMM, ev, prior):
"""[Figure 15.5]
Viterbi algorithm to find the most likely sequence. Computes the best path,
given an HMM model and a sequence of observations."""
t = len(ev)
ev.insert(0, None)

m = [[0.0, 0.0] for _ in range(len(ev) - 1)]

# the recursion is initialized with m1 = forward(P(X0), e1)
m[0] = forward(HMM, prior, ev[1])

for i in range(1, t):
m[i] = element_wise_product(HMM.sensor_dist(ev[i + 1]),
[max(element_wise_product(HMM.transition_model[0], m[i - 1])),
max(element_wise_product(HMM.transition_model[1], m[i - 1]))])

path = [0.0] * (len(ev) - 1)
# the construction of the most likely sequence starts in the final state with the largest probability,
# and runs backwards; the algorithm needs to store for each xt its best predecessor xt-1
for i in range(t, -1, -1):
path[i - 1] = max(m[i - 1])

return path


# _________________________________________________________________________


Expand Down Expand Up @@ -702,6 +743,7 @@ def fixed_lag_smoothing(e_t, HMM, d, ev, t):
else:
return None


# _________________________________________________________________________


Expand Down Expand Up @@ -742,13 +784,15 @@ def particle_filtering(e, N, HMM):

return s


# _________________________________________________________________________
## TODO: Implement continuous map for MonteCarlo similar to Fig25.10 from the book
# TODO: Implement continuous map for MonteCarlo similar to Fig25.10 from the book


class MCLmap:
"""Map which provides probability distributions and sensor readings.
Consists of discrete cells which are either an obstacle or empty"""

def __init__(self, m):
self.m = m
self.nrows = len(m)
Expand All @@ -772,7 +816,7 @@ def ray_cast(self, sensor_num, kin_state):
# 0
# 3R1
# 2
delta = ((sensor_num % 2 == 0)*(sensor_num - 1), (sensor_num % 2 == 1)*(2 - sensor_num))
delta = ((sensor_num % 2 == 0) * (sensor_num - 1), (sensor_num % 2 == 1) * (2 - sensor_num))
# sensor direction changes based on orientation
for _ in range(orient):
delta = (delta[1], -delta[0])
Expand All @@ -790,9 +834,9 @@ def ray_cast(sensor_num, kin_state, m):
return m.ray_cast(sensor_num, kin_state)

M = len(z)
W = [0]*N
S_ = [0]*N
W_ = [0]*N
W = [0] * N
S_ = [0] * N
W_ = [0] * N
v = a['v']
w = a['w']

Expand Down
6 changes: 6 additions & 0 deletions tests/test_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,13 @@ def test_unify():
assert unify(x & 4 & y, 6 & y & 4, {}) == {x: 6, y: 4}
assert unify(expr('A(x)'), expr('A(B)')) == {x: B}
assert unify(expr('American(x) & Weapon(B)'), expr('American(A) & Weapon(y)')) == {x: A, y: B}
assert unify(expr('P(F(x,z), G(u, z))'), expr('P(F(y,a), y)')) == {x: G(u, a), z: a, y: G(u, a)}

# test for https://github.com/aimacode/aima-python/issues/1053
# unify(expr('P(A, x, F(G(y)))'), expr('P(z, F(z), F(u))'))
# must return {z: A, x: F(A), u: G(y)} and not {z: A, x: F(z), u: G(y)}
assert unify(expr('P(A, x, F(G(y)))'), expr('P(z, F(z), F(u))')) == {z: A, x: F(A), u: G(y)}
assert unify(expr('P(x, A, F(G(y)))'), expr('P(F(z), z, F(u))')) == {x: F(A), z: A, u: G(y)}

def test_pl_fc_entails():
assert pl_fc_entails(horn_clauses_KB, expr('Q'))
Expand Down
Loading