Skip to content

Commit 52f46c7

Browse files
committed
rebesed to resolve comflicts in utils.py
2 parents cd0657d + 5ee8c4a commit 52f46c7

File tree

9 files changed

+102
-51
lines changed

9 files changed

+102
-51
lines changed

README.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
1-
# `aima-python`: Structure of the Project
1+
# ![](https://github.com/aimacode/aima-java/blob/gh-pages/aima3e/images/aima3e.jpg)`aima-python` (Python 3.5)
22

3-
Python code for the book *Artificial Intelligence: A Modern Approach.*
4-
When complete, this project will cover all the major topics in the book, for each topic, such as `logic`, we will have the following [Python 3.5](https://www.python.org/downloads/release/python-350/) files in the main branch:
53

6-
- `logic.py`: Implementations of all the pseudocode algorithms in the book.
7-
- `logic_test.py`: A lightweight test suite, using `assert` statements, designed for use with `py.test`.
4+
Python code for the book *Artificial Intelligence: A Modern Approach.* We're loooking for one student sponsored by Google Summer of Code (GSoC) to work on this project; if you want to be that student, make some good contributions here (by looking throush the "Issues" and resolving some), and submit an application. (And we're always looking for solid contributors who are not affiliated with GSoC.)
5+
6+
## Structure of the Project
7+
8+
When complete, this project will have [Python 3.5](https://www.python.org/downloads/release/python-350/) code for all the pseudocode algorithms in the book. For each major topic, such as `logic`, we will have the following files in the main branch:
9+
10+
- `logic.py`: Implementations of all the pseudocode algorithms, and necessary support functions/classes/data.
11+
- `logic_test.py`: A lightweight test suite, using `assert` statements, designed for use with [`py.test`](http://pytest.org/latest/).
812
- `logic.ipynb`: A Jupyter notebook, with examples of usage. Does a `from logic import *` to get the code.
913

10-
Until we get there, we will support a legacy branch, `aima3python2` (for the third edition of the textbook and for Python 2 code). To prepare code for the new master branch, the following two steps should be taken
14+
Until we get there, we will support a legacy branch, `aima3python2` (for the third edition of the textbook and for Python 2 code). To prepare code for the new master branch, the following two steps should be taken:
1115

1216
## Port to Python 3; Pythonic Idioms; py.test
1317

1418
- Check for common problems in [porting to Python 3](http://python3porting.com/problems.html), such as: `print` is now a function; `range` and `map` and other functions no longer produce `list`s; objects of different types can no longer be compared with `<`; strings are now Unicode; it would be nice to move `%` string formating to `.format`; there is a new `next` function for generators; integer division now returns a float; we can now use set literals.
15-
- Replace poor idioms with proper Python. For example, we have many functions that were taken directly from Common Lisp, such as the `every` function: `every(callable, items)` returns true if every element of `items` is callable. This is good Lisp style, but good Python style would be to use `all` and a generator expression: `all(callable(f) for f in items)`. Eventually, fix all calls to these legacy Lisp functions and then remove the functions.
19+
- Replace old Lisp-based idioms with proper Python idioms. For example, we have many functions that were taken directly from Common Lisp, such as the `every` function: `every(callable, items)` returns true if every element of `items` is callable. This is good Lisp style, but good Python style would be to use `all` and a generator expression: `all(callable(f) for f in items)`. Eventually, fix all calls to these legacy Lisp functions and then remove the functions.
1620
- Create a `_test.py` file, and define functions that use `assert` to make tests. Remove any old `doctest` tests.
17-
In other words, replace the ">>> 2 + 2" in a docstring with "assert 2 + 2 == 4" in `filename_test.py`.
21+
In other words, replace the ">>> 2 + 2 \n 4" in a docstring with "assert 2 + 2 == 4" in `filename_test.py`.
1822

1923
## New and Improved Algorithms
2024

21-
- Implement functions that were in the third edition of the book but were not yet implemented in the code.
22-
- As we finish chapters for the new fourth edition, we will share the pseudocode, and describe what changes are necessary.
25+
- Implement functions that were in the third edition of the book but were not yet implemented in the code. Check the [list of pseudocode algorithms (pdf)](http://aima.cs.berkeley.edu/algorithms.pdf) to see what's missing.
26+
- As we finish chapters for the new fourth edition, we will share the new pseudocode, and describe what changes are necessary.
2327

2428
- Create a `.ipynb` notebook, and give examples of how to use the code.
2529

@@ -47,7 +51,7 @@ Beyond the above rules, we use [Pep 8](https://www.python.org/dev/peps/pep-0008)
4751
# Choice of Programming Languages
4852

4953
Are we right to concentrate on Java and Python versions of the code? I think so; both languages are popular; Java is
50-
fast enough for our purposes, and has reasonable type declarations (but can be verbose); Python is popular and has a very direct mapping to the pseudocode in the book (ut lacks type declarations and can be solw). The [TIOBE Index](http://www.tiobe.com/tiobe_index) says the top five most popular languages are:
54+
fast enough for our purposes, and has reasonable type declarations (but can be verbose); Python is popular and has a very direct mapping to the pseudocode in the book (but lacks type declarations and can be slow). The [TIOBE Index](http://www.tiobe.com/tiobe_index) says the top five most popular languages are:
5155

5256
Java, C, C++, C#, Python
5357

csp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ def __init__(self, grid):
530530
the digits 1-9 denote a filled cell, '.' or '0' an empty one;
531531
other characters are ignored."""
532532
squares = iter(re.findall(r'\d|\.', grid))
533-
domains = dict((var, if_(ch in '123456789', [ch], '123456789'))
533+
domains = dict((var, ([ch] if ch in '123456789' else '123456789'))
534534
for var, ch in zip(flatten(self.rows), squares))
535535
for _ in squares:
536536
raise ValueError("Not a Sudoku grid", grid) # Too many squares

games.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def alphabeta_full_search(state, game):
4141

4242
player = game.to_move(state)
4343

44+
#Functions used by alphabeta
4445
def max_value(state, alpha, beta):
4546
if game.terminal_test(state):
4647
return game.utility(state, player)
@@ -64,16 +65,15 @@ def min_value(state, alpha, beta):
6465
return v
6566

6667
# Body of alphabeta_search:
67-
return argmax(game.actions(state),
68-
lambda a: min_value(game.result(state, a),
69-
-infinity, infinity))
68+
return max_value(state, -infinity, infinity)
7069

7170
def alphabeta_search(state, game, d=4, cutoff_test=None, eval_fn=None):
7271
"""Search game to determine best action; use alpha-beta pruning.
7372
This version cuts off search and uses an evaluation function."""
7473

7574
player = game.to_move(state)
7675

76+
#Functions used by alphabeta
7777
def max_value(state, alpha, beta, depth):
7878
if cutoff_test(state, depth):
7979
return eval_fn(state)
@@ -103,9 +103,7 @@ def min_value(state, alpha, beta, depth):
103103
cutoff_test = (cutoff_test or
104104
(lambda state,depth: depth>d or game.terminal_test(state)))
105105
eval_fn = eval_fn or (lambda state: game.utility(state, player))
106-
return argmax(game.actions(state),
107-
lambda a: min_value(game.result(state, a),
108-
-infinity, infinity, 0))
106+
return max_value(state, -infinity, infinity, 0)
109107

110108
#______________________________________________________________________________
111109
# Players for Games
@@ -207,7 +205,7 @@ def terminal_test(self, state):
207205
return state not in ('A', 'B', 'C', 'D')
208206

209207
def to_move(self, state):
210-
return if_(state in 'BCD', 'MIN', 'MAX')
208+
return ('MIN' if state in 'BCD' else 'MAX')
211209

212210
class TicTacToe(Game):
213211
"""Play TicTacToe on an h x v board, with Max (first player) playing 'X'.
@@ -229,13 +227,13 @@ def result(self, state, move):
229227
return state # Illegal move has no effect
230228
board = state.board.copy(); board[move] = state.to_move
231229
moves = list(state.moves); moves.remove(move)
232-
return Struct(to_move=if_(state.to_move == 'X', 'O', 'X'),
230+
return Struct(to_move=('O' if state.to_move == 'X' else 'X'),
233231
utility=self.compute_utility(board, move, state.to_move),
234232
board=board, moves=moves)
235233

236234
def utility(self, state, player):
237235
"Return the value to player; 1 for win, -1 for loss, 0 otherwise."
238-
return if_(player == 'X', state.utility, -state.utility)
236+
return (state.utility if player == 'X' else -state.utility)
239237

240238
def terminal_test(self, state):
241239
"A state is terminal if it is won or there are no empty squares."
@@ -254,7 +252,7 @@ def compute_utility(self, board, move, player):
254252
self.k_in_row(board, move, player, (1, 0)) or
255253
self.k_in_row(board, move, player, (1, -1)) or
256254
self.k_in_row(board, move, player, (1, 1))):
257-
return if_(player == 'X', +1, -1)
255+
return (+1 if player == 'X' else -1)
258256
else:
259257
return 0
260258

grid.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
## OK, the following are not as widely useful utilities as some of the other
2+
## functions here, but they do show up wherever we have 2D grids: Wumpus and
3+
## Vacuum worlds, TicTacToe and Checkers, and markov decision Processes.
4+
##__________________________________________________________________________
5+
import math
6+
7+
8+
orientations = [(1, 0), (0, 1), (-1, 0), (0, -1)]
9+
10+
def turn_heading(heading, inc, headings=orientations):
11+
return headings[(headings.index(heading) + inc) % len(headings)]
12+
13+
def turn_right(heading):
14+
return turn_heading(heading, -1)
15+
16+
def turn_left(heading):
17+
return turn_heading(heading, +1)
18+
19+
def distance(a, b):
20+
"""The distance between two (x, y) points.
21+
>>> distance((1,2),(5,5))
22+
5.0
23+
"""
24+
return math.hypot((a[0] - b[0]), (a[1] - b[1]))
25+
26+
def distance_squared(a, b):
27+
"""The square of the distance between two (x, y) points.
28+
>>> distance_squared((1,2),(5,5))
29+
25.0
30+
"""
31+
return (a[0]- b[0])**2 + (a[1] - b[1])**2
32+
33+
def distance2(a, b):
34+
"The square of the distance between two (x, y) points."
35+
return distance_squared(a, b)
36+
37+
def clip(x, lowest, highest):
38+
"""Return x clipped to the range [lowest..highest].
39+
>>> [clip(x, 0, 1) for x in [-1, 0.5, 10]]
40+
[0, 0.5, 1]
41+
"""
42+
return max(lowest, min(x, highest))
43+
44+
45+
def vector_clip(vector, lowest, highest):
46+
"""Return vector, except if any element is less than the corresponding
47+
value of lowest or more than the corresponding value of highest, clip to
48+
those values.
49+
>>> vector_clip((-1, 10), (0, 0), (9, 9))
50+
(0, 9)
51+
"""
52+
return type(vector)(map(clip, vector, lowest, highest))

logic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ def WalkSAT(clauses, p=0.5, max_flips=10000):
743743
for i in range(max_flips):
744744
satisfied, unsatisfied = [], []
745745
for clause in clauses:
746-
if_(pl_true(clause, model), satisfied, unsatisfied).append(clause)
746+
(satisfied if pl_true(clause, model) else unsatisfied).append(clause)
747747
if not unsatisfied: ## if model satisfies all the clauses
748748
return model
749749
clause = random.choice(unsatisfied)

mdp.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ def __init__(self, init, actlist, terminals, gamma=.9):
2121
self.init=init
2222
self.actlist=actlist
2323
self.terminals=terminals
24+
if not (0 <= gamma < 1):
25+
raise ValueError("An MDP must have 0 <= gamma < 1")
2426
self.gamma=gamma
2527
self.states=set()
2628
self.reward={}
@@ -72,7 +74,7 @@ def T(self, state, action):
7274
def go(self, state, direction):
7375
"Return the state that results from going in this direction."
7476
state1 = vector_add(state, direction)
75-
return if_(state1 in self.states, state1, state)
77+
return (state1 if state1 in self.states else state)
7678

7779
def to_grid(self, mapping):
7880
"""Convert a mapping from (x, y) to v into a [[..., v, ...]] grid."""

probability.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def p(self, value, event):
236236
0.375"""
237237
assert isinstance(value, bool)
238238
ptrue = self.cpt[event_values(event, self.parents)]
239-
return if_(value, ptrue, 1 - ptrue)
239+
return (ptrue if value else 1 - ptrue)
240240

241241
def sample(self, event):
242242
"""Sample from the distribution for this variable conditioned

search.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ def recursive_dls(node, problem, limit):
246246
cutoff_occurred = True
247247
elif result is not None:
248248
return result
249-
return if_(cutoff_occurred, 'cutoff', None)
249+
return ('cutoff' if cutoff_occurred else None)
250250

251251
# Body of depth_limited_search:
252252
return recursive_dls(Node(problem.initial), problem, limit)
@@ -321,7 +321,7 @@ def hill_climbing(problem):
321321

322322
def exp_schedule(k=20, lam=0.005, limit=100):
323323
"One possible schedule function for simulated annealing"
324-
return lambda t: if_(t < limit, k * math.exp(-lam * t), 0)
324+
return lambda t: (k * math.exp(-lam * t) if t < limit else 0)
325325

326326
def simulated_annealing(problem, schedule=exp_schedule()):
327327
"[Fig. 4.5]"

utils.py

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
33
TODO[COMPLETED]: Let's take the >>> doctest examples out of the docstrings, and put them in utils_test.py
44
TODO: count_if and the like are leftovers from COmmon Lisp; let's make replace thenm with Pythonic alternatives.
5-
TODO: if_ is a terrible idea; replace all uses with (x if test else y) and remove if_
65
TODO: Create a separate grid.py file for 2D grid environments; move headings, etc there.
76
TODO: Priority queues may not belong here -- see treatment in search.py
87
"""
@@ -63,7 +62,10 @@ def unique(seq):
6362

6463
def product(numbers):
6564
"""Return the product of the numbers."""
66-
return reduce(operator.mul, numbers, 1)
65+
result=1
66+
for i in numbers:
67+
result=result*i
68+
return result
6769

6870
def count_if(predicate, seq):
6971
"""Count the number of elements of seq for which the predicate is true."""
@@ -233,17 +235,26 @@ def turn_right(heading):
233235
def turn_left(heading):
234236
return turn_heading(heading, +1)
235237

236-
def distance(a, b):
237-
"The distance between two (x, y) points."
238-
return math.hypot((a.x - b.x), (a.y - b.y))
238+
def Point(x, y):
239+
return (x, y)
240+
241+
def point_x(point):
242+
return point[0]
239243

240-
def distance_squared(a, b):
244+
def point_y(point):
245+
return point[1]
246+
247+
def distance(a, b):
241248
"The distance between two (x, y) points."
242-
return (a.x - b.x)**2 + (a.y - b.y)**2
249+
ax, ay = a
250+
bx, by = b
251+
return math.hypot((ax - bx), (ay - by))
243252

244253
def distance2(a, b):
245254
"The square of the distance between two (x, y) points."
246-
return distance_squared(a, b)
255+
ax, ay = a
256+
bx, by = b
257+
return (ax - bx)**2 + (ay - by)**2
247258

248259
def vector_clip(vector, lowest, highest):
249260
"""Return vector, except if any element is less than the corresponding
@@ -291,22 +302,6 @@ def memoized_fn(*args):
291302

292303
return memoized_fn
293304

294-
def if_(test, result, alternative):
295-
"""Like C++ and Java's (test ? result : alternative), except
296-
both result and alternative are always evaluated. However, if
297-
either evaluates to a function, it is applied to the empty arglist,
298-
so you can delay execution by putting it in a lambda.
299-
"""
300-
if test:
301-
if callable(result):
302-
return result()
303-
304-
return result
305-
else:
306-
if callable(alternative):
307-
return alternative()
308-
309-
return alternative
310305

311306
def name(obj):
312307
"Try to find some reasonable name for the object."

0 commit comments

Comments
 (0)