diff --git a/.flake8 b/.flake8
index 405ab746c..c944f27ed 100644
--- a/.flake8
+++ b/.flake8
@@ -1,3 +1,4 @@
[flake8]
max-line-length = 100
ignore = E121,E123,E126,E221,E222,E225,E226,E242,E701,E702,E704,E731,W503,F405
+exclude = tests
diff --git a/.travis.yml b/.travis.yml
index e6563f0fe..49270ad2a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,6 +16,7 @@ install:
script:
- py.test
- python -m doctest -v *.py
+ - flake8 .
after_success:
- flake8 --max-line-length 100 --ignore=E121,E123,E126,E221,E222,E225,E226,E242,E701,E702,E704,E731,W503 .
diff --git a/agents.py b/agents.py
index 047eb3fd6..403bfbddc 100644
--- a/agents.py
+++ b/agents.py
@@ -162,6 +162,7 @@ def rule_match(state, rules):
# ______________________________________________________________________________
+
loc_A, loc_B = (0, 0), (1, 0) # The two locations for the Vacuum world
@@ -394,8 +395,9 @@ def things_near(self, location, radius=None):
if radius is None:
radius = self.perceptible_distance
radius2 = radius * radius
- return [(thing, radius2 - distance_squared(location, thing.location)) for thing in self.things
- if distance_squared(location, thing.location) <= radius2]
+ return [(thing, radius2 - distance_squared(location, thing.location))
+ for thing in self.things if distance_squared(
+ location, thing.location) <= radius2]
def percept(self, agent):
"""By default, agent perceives things within a default radius."""
@@ -435,33 +437,28 @@ def move_to(self, thing, destination):
t.location = destination
return thing.bump
- # def add_thing(self, thing, location=(1, 1)):
- # super(XYEnvironment, self).add_thing(thing, location)
- # thing.holding = []
- # thing.held = None
- # for obs in self.observers:
- # obs.thing_added(thing)
-
def add_thing(self, thing, location=(1, 1), exclude_duplicate_class_items=False):
"""Adds things to the world. If (exclude_duplicate_class_items) then the item won't be
added if the location has at least one item of the same class."""
if (self.is_inbounds(location)):
if (exclude_duplicate_class_items and
- any(isinstance(t, thing.__class__) for t in self.list_things_at(location))):
- return
+ any(isinstance(t, thing.__class__) for t in self.list_things_at(location))):
+ return
super().add_thing(thing, location)
def is_inbounds(self, location):
"""Checks to make sure that the location is inbounds (within walls if we have walls)"""
- x,y = location
+ x, y = location
return not (x < self.x_start or x >= self.x_end or y < self.y_start or y >= self.y_end)
def random_location_inbounds(self, exclude=None):
"""Returns a random location that is inbounds (within walls if we have walls)"""
- location = (random.randint(self.x_start, self.x_end), random.randint(self.y_start, self.y_end))
+ location = (random.randint(self.x_start, self.x_end),
+ random.randint(self.y_start, self.y_end))
if exclude is not None:
while(location == exclude):
- location = (random.randint(self.x_start, self.x_end), random.randint(self.y_start, self.y_end))
+ location = (random.randint(self.x_start, self.x_end),
+ random.randint(self.y_start, self.y_end))
return location
def delete_thing(self, thing):
@@ -514,6 +511,7 @@ class Wall(Obstacle):
# ______________________________________________________________________________
+
try:
from ipythonblocks import BlockGrid
from IPython.display import HTML, display
@@ -521,12 +519,13 @@ class Wall(Obstacle):
except:
pass
+
class GraphicEnvironment(XYEnvironment):
def __init__(self, width=10, height=10, boundary=True, color={}, display=False):
"""define all the usual XYEnvironment characteristics,
but initialise a BlockGrid for GUI too"""
super().__init__(width, height)
- self.grid = BlockGrid(width, height, fill=(200,200,200))
+ self.grid = BlockGrid(width, height, fill=(200, 200, 200))
if display:
self.grid.show()
self.visible = True
@@ -535,11 +534,6 @@ def __init__(self, width=10, height=10, boundary=True, color={}, display=False):
self.bounded = boundary
self.colors = color
- #def list_things_at(self, location, tclass=Thing): # need to override because locations
- # """Return all things exactly at a given location."""
- # return [thing for thing in self.things
- # if thing.location == location and isinstance(thing, tclass)]
-
def get_world(self):
"""Returns all the items in the world in a format
understandable by the ipythonblocks BlockGrid"""
@@ -589,23 +583,17 @@ def update(self, delay=1):
def reveal(self):
"""display the BlockGrid for this world - the last thing to be added
at a location defines the location color"""
- #print("Grid={}".format(self.grid))
self.draw_world()
- #if not self.visible == True:
- # self.grid.show()
self.grid.show()
- self.visible == True
+ self.visible = True
def draw_world(self):
self.grid[:] = (200, 200, 200)
world = self.get_world()
- #print("world {}".format(world))
for x in range(0, len(world)):
for y in range(0, len(world[x])):
if len(world[x][y]):
self.grid[y, x] = self.colors[world[x][y][-1].__class__.__name__]
- #print('location: ({}, {}) got color: {}'
- #.format(y, x, self.colors[world[x][y][-1].__class__.__name__]))
def conceal(self):
"""hide the BlockGrid for this world"""
@@ -613,10 +601,6 @@ def conceal(self):
display(HTML(''))
-
-
-
-
# ______________________________________________________________________________
# Continuous environment
@@ -733,21 +717,27 @@ def __eq__(self, rhs):
return rhs.__class__ == Gold
pass
+
class Bump(Thing):
pass
+
class Glitter(Thing):
pass
+
class Pit(Thing):
pass
+
class Breeze(Thing):
pass
+
class Arrow(Thing):
pass
+
class Scream(Thing):
pass
@@ -756,6 +746,7 @@ class Wumpus(Agent):
screamed = False
pass
+
class Stench(Thing):
pass
@@ -772,7 +763,7 @@ def can_grab(self, thing):
class WumpusEnvironment(XYEnvironment):
- pit_probability = 0.2 # Probability to spawn a pit in a location. (From Chapter 7.2)
+ pit_probability = 0.2 # Probability to spawn a pit in a location. (From Chapter 7.2)
# Room should be 4x4 grid of rooms. The extra 2 for walls
def __init__(self, agent_program, width=6, height=6):
@@ -805,7 +796,6 @@ def init_world(self, program):
"GOLD"
self.add_thing(Gold(), self.random_location_inbounds(exclude=(1, 1)), True)
- #self.add_thing(Gold(), (2,1), True) Making debugging a whole lot easier
"AGENT"
self.add_thing(Explorer(program), (1, 1), True)
@@ -814,7 +804,12 @@ def get_world(self, show_walls=True):
"""Returns the items in the world"""
result = []
x_start, y_start = (0, 0) if show_walls else (1, 1)
- x_end, y_end = (self.width, self.height) if show_walls else (self.width - 1, self.height - 1)
+
+ if show_walls:
+ x_end, y_end = self.width, self.height
+ else:
+ x_end, y_end = self.width - 1, self.height - 1
+
for x in range(x_start, x_end):
row = []
for y in range(y_start, y_end):
@@ -837,7 +832,6 @@ def percepts_from(self, agent, location, tclass=Thing):
if location != agent.location:
thing_percepts[Gold] = None
-
result = [thing_percepts.get(thing.__class__, thing) for thing in self.things
if thing.location == location and isinstance(thing, tclass)]
return result if len(result) else [None]
@@ -916,18 +910,19 @@ def in_danger(self, agent):
def is_done(self):
"""The game is over when the Explorer is killed
or if he climbs out of the cave only at (1,1)."""
- explorer = [agent for agent in self.agents if isinstance(agent, Explorer) ]
+ explorer = [agent for agent in self.agents if isinstance(agent, Explorer)]
if len(explorer):
if explorer[0].alive:
- return False
+ return False
else:
print("Death by {} [-1000].".format(explorer[0].killed_by))
else:
print("Explorer climbed out {}."
- .format("with Gold [+1000]!" if Gold() not in self.things else "without Gold [+0]"))
+ .format(
+ "with Gold [+1000]!" if Gold() not in self.things else "without Gold [+0]"))
return True
- #Almost done. Arrow needs to be implemented
+ # Almost done. Arrow needs to be implemented
# ______________________________________________________________________________
@@ -952,6 +947,7 @@ def score(env):
# _________________________________________________________________________
+
__doc__ += """
>>> a = ReflexVacuumAgent()
>>> a.program((loc_A, 'Clean'))
diff --git a/canvas.py b/canvas.py
index 213e38cc9..318155bea 100644
--- a/canvas.py
+++ b/canvas.py
@@ -1,4 +1,4 @@
-from IPython.display import HTML, display, clear_output
+from IPython.display import HTML, display
_canvas = """
@@ -7,7 +7,8 @@
-"""
+""" # noqa
+
class Canvas:
"""Inherit from this class to manage the HTML canvas element in jupyter notebooks.
@@ -81,9 +82,10 @@ def arc(self, x, y, r, start, stop):
"Draw an arc with (x, y) as centre, 'r' as radius from angles 'start' to 'stop'"
self.execute("arc({0}, {1}, {2}, {3}, {4})".format(x, y, r, start, stop))
- def arc_n(self, xn ,yn, rn, start, stop):
+ def arc_n(self, xn, yn, rn, start, stop):
"""Similar to arc(), but the dimensions are normalized to fall between 0 and 1
- The normalizing factor for radius is selected between width and height by seeing which is smaller
+ The normalizing factor for radius is selected between width and height by
+ seeing which is smaller
"""
x = round(xn * self.width)
y = round(yn * self.height)
diff --git a/csp.py b/csp.py
index 8c5ecde3d..deb1efc12 100644
--- a/csp.py
+++ b/csp.py
@@ -414,6 +414,7 @@ def parse_neighbors(neighbors, variables=[]):
dic[B].append(A)
return dic
+
australia = MapColoringCSP(list('RGB'),
'SA: WA NT Q NSW V; NT: WA Q; NSW: Q V; T: ')
@@ -584,7 +585,8 @@ class Sudoku(CSP):
>>> h = Sudoku(harder1)
>>> backtracking_search(h, select_unassigned_variable=mrv, inference=forward_checking) is not None
True
- """
+ """ # noqa
+
R3 = _R3
Cell = _CELL
bgrid = _BGRID
diff --git a/games.py b/games.py
index d98b7473c..205d8e6ee 100644
--- a/games.py
+++ b/games.py
@@ -196,7 +196,7 @@ def display(self, state):
def __repr__(self):
return '<{}>'.format(self.__class__.__name__)
-
+
def play_game(self, *players):
"""Play an n-person, move-alternating game."""
state = self.initial
@@ -259,8 +259,8 @@ def actions(self, state):
def result(self, state, move):
if move not in state.moves:
return GameState(to_move=('O' if state.to_move == 'X' else 'X'),
- utility=self.compute_utility(state.board, move, state.to_move),
- board=state.board, moves=state.moves) # Illegal move has no effect
+ utility=self.compute_utility(state.board, move, state.to_move),
+ board=state.board, moves=state.moves) # Illegal move has no effect
board = state.board.copy()
board[move] = state.to_move
moves = list(state.moves)
@@ -327,7 +327,8 @@ class Canvas_TicTacToe(Canvas):
"""Play a 3x3 TicTacToe game on HTML canvas
TODO: Add restart button
"""
- def __init__(self, varname, player_1='human', player_2='random', id=None, width=300, height=300):
+ def __init__(self, varname, player_1='human', player_2='random', id=None,
+ width=300, height=300):
valid_players = ('human', 'random', 'alphabeta')
if player_1 not in valid_players or player_2 not in valid_players:
raise TypeError("Players must be one of {}".format(valid_players))
@@ -381,7 +382,8 @@ def draw_board(self):
else:
self.text_n('Player {} wins!'.format(1 if utility > 0 else 2), 0.1, 0.1)
else: # Print which player's turn it is
- self.text_n("Player {}'s move({})".format(self.turn+1, self.players[self.turn]), 0.1, 0.1)
+ self.text_n("Player {}'s move({})".format(self.turn+1, self.players[self.turn]),
+ 0.1, 0.1)
self.update()
diff --git a/ipyviews.py b/ipyviews.py
index 4c3776fbc..fbdc9a580 100644
--- a/ipyviews.py
+++ b/ipyviews.py
@@ -20,7 +20,7 @@
var all_polygons = {3};
{4}
-'''
+''' # noqa
with open('js/continuousworld.js', 'r') as js_file:
_JS_CONTINUOUS_WORLD = js_file.read()
@@ -61,7 +61,9 @@ def get_polygon_obstacles_coordinates(self):
def show(self):
clear_output()
- total_html = _CONTINUOUS_WORLD_HTML.format(self.width, self.height, self.object_name(), str(self.get_polygon_obstacles_coordinates()), _JS_CONTINUOUS_WORLD)
+ total_html = _CONTINUOUS_WORLD_HTML.format(self.width, self.height, self.object_name(),
+ str(self.get_polygon_obstacles_coordinates()),
+ _JS_CONTINUOUS_WORLD)
display(HTML(total_html))
diff --git a/learning.py b/learning.py
index 121f184c3..ec685131d 100644
--- a/learning.py
+++ b/learning.py
@@ -12,10 +12,11 @@
import random
from statistics import mean
-from collections import defaultdict, Counter
+from collections import defaultdict
# ______________________________________________________________________________
+
def rms_error(predictions, targets):
return math.sqrt(ms_error(predictions, targets))
@@ -160,15 +161,15 @@ def sanitize(self, example):
return [attr_i if i in self.inputs else None
for i, attr_i in enumerate(example)]
- def classes_to_numbers(self,classes=None):
+ def classes_to_numbers(self, classes=None):
"""Converts class names to numbers."""
if not classes:
# If classes were not given, extract them from values
classes = sorted(self.values[self.target])
for item in self.examples:
item[self.target] = classes.index(item[self.target])
-
- def remove_examples(self,value=""):
+
+ def remove_examples(self, value=""):
"""Remove examples that contain given value."""
self.examples = [x for x in self.examples if value not in x]
self.update_values()
@@ -383,7 +384,7 @@ def plurality_value(examples):
def count(attr, val, examples):
"""Count the number of examples that have attr = val."""
- return sum(e[attr] == val for e in examples) #count(e[attr] == val for e in examples)
+ return sum(e[attr] == val for e in examples)
def all_same_class(examples):
"""Are all these examples in the same target class?"""
@@ -877,6 +878,7 @@ def score(learner, size):
# ______________________________________________________________________________
# The rest of this file gives datasets for machine learning problems.
+
orings = DataSet(name='orings', target='Distressed',
attrnames="Rings Distressed Temp Pressure Flightnum")
@@ -900,6 +902,7 @@ def RestaurantDataSet(examples=None):
attrnames='Alternate Bar Fri/Sat Hungry Patrons Price ' +
'Raining Reservation Type WaitEstimate Wait')
+
restaurant = RestaurantDataSet()
@@ -909,28 +912,29 @@ def T(attrname, branches):
for value, child in branches.items()}
return DecisionFork(restaurant.attrnum(attrname), attrname, branches)
+
""" [Figure 18.2]
A decision tree for deciding whether to wait for a table at a hotel.
"""
waiting_decision_tree = T('Patrons',
- {'None': 'No', 'Some': 'Yes', 'Full':
- T('WaitEstimate',
- {'>60': 'No', '0-10': 'Yes',
- '30-60':
- T('Alternate', {'No':
- T('Reservation', {'Yes': 'Yes', 'No':
- T('Bar', {'No': 'No',
- 'Yes': 'Yes'
- })}),
- 'Yes':
- T('Fri/Sat', {'No': 'No', 'Yes': 'Yes'})}),
- '10-30':
- T('Hungry', {'No': 'Yes', 'Yes':
- T('Alternate',
- {'No': 'Yes', 'Yes':
- T('Raining', {'No': 'No', 'Yes': 'Yes'})
- })})})})
+ {'None': 'No', 'Some': 'Yes',
+ 'Full': T('WaitEstimate',
+ {'>60': 'No', '0-10': 'Yes',
+ '30-60': T('Alternate',
+ {'No': T('Reservation',
+ {'Yes': 'Yes',
+ 'No': T('Bar', {'No': 'No',
+ 'Yes': 'Yes'})}),
+ 'Yes': T('Fri/Sat', {'No': 'No', 'Yes': 'Yes'})}
+ ),
+ '10-30': T('Hungry',
+ {'No': 'Yes',
+ 'Yes': T('Alternate',
+ {'No': 'Yes',
+ 'Yes': T('Raining',
+ {'No': 'No',
+ 'Yes': 'Yes'})})})})})
def SyntheticRestaurant(n=20):
diff --git a/logic.py b/logic.py
index bd9c92334..68d996c14 100644
--- a/logic.py
+++ b/logic.py
@@ -33,7 +33,7 @@
from utils import (
removeall, unique, first, argmax, probability,
- isnumber, issequence, Symbol, Expr, expr, subexpressions
+ isnumber, issequence, Expr, expr, subexpressions
)
import agents
@@ -180,6 +180,7 @@ def parse_definite_clause(s):
antecedent, consequent = s.args
return conjuncts(antecedent), consequent
+
# Useful constant Exprs used in examples and code:
A, B, C, D, E, F, G, P, Q, x, y, z = map(Expr, 'ABCDEFGPQxyz')
@@ -391,6 +392,7 @@ def associate(op, args):
else:
return Expr(op, *args)
+
_op_identity = {'&': True, '|': False, '+': 0, '*': 1}
@@ -511,6 +513,7 @@ def pl_fc_entails(KB, q):
agenda.append(c.args[1])
return False
+
""" [Figure 7.13]
Simple inference in a wumpus world example
"""
@@ -707,7 +710,8 @@ def translate_to_SAT(init, transition, goal, time):
s_ = transition[s][action]
for t in range(time):
# Action 'action' taken from state 's' at time 't' to reach 's_'
- action_sym[s, action, t] = Expr("Transition_{}".format(next(transition_counter)))
+ action_sym[s, action, t] = Expr(
+ "Transition_{}".format(next(transition_counter)))
# Change the state from s to s_
clauses.append(action_sym[s, action, t] |'==>'| state_sym[s, t])
@@ -732,7 +736,7 @@ def translate_to_SAT(init, transition, goal, time):
clauses.append(associate('|', [action_sym[tr] for tr in transitions_t]))
for tr in transitions_t:
- for tr_ in transitions_t[transitions_t.index(tr) + 1 :]:
+ for tr_ in transitions_t[transitions_t.index(tr) + 1:]:
# there cannot be two transitions tr and tr_ at time t
clauses.append(~action_sym[tr] | ~action_sym[tr_])
@@ -877,6 +881,7 @@ def standardize_variables(sentence, dic=None):
return Expr(sentence.op,
*[standardize_variables(a, dic) for a in sentence.args])
+
standardize_variables.counter = itertools.count()
# ______________________________________________________________________________
diff --git a/mdp.py b/mdp.py
index 2854d0616..902582b19 100644
--- a/mdp.py
+++ b/mdp.py
@@ -6,7 +6,7 @@
dictionary of {state:number} pairs. We then define the value_iteration
and policy_iteration algorithms."""
-from utils import argmax, vector_add, print_table
+from utils import argmax, vector_add, print_table # noqa
from grid import orientations, turn_right, turn_left
import random
@@ -97,12 +97,13 @@ def to_arrows(self, policy):
# ______________________________________________________________________________
+
""" [Figure 17.1]
A 4x3 grid environment that presents the agent with a sequential decision problem.
"""
sequential_decision_environment = GridMDP([[-0.04, -0.04, -0.04, +1],
- [-0.04, None, -0.04, -1],
+ [-0.04, None, -0.04, -1],
[-0.04, -0.04, -0.04, -0.04]],
terminals=[(3, 2), (3, 1)])
@@ -165,6 +166,7 @@ def policy_evaluation(pi, U, mdp, k=20):
U[s] = R(s) + gamma * sum([p * U[s1] for (p, s1) in T(s, pi[s])])
return U
+
__doc__ += """
>>> pi = best_policy(sequential_decision_environment, value_iteration(sequential_decision_environment, .01))
@@ -180,4 +182,4 @@ def policy_evaluation(pi, U, mdp, k=20):
> > > .
^ None ^ .
^ > ^ <
-"""
+""" # noqa
diff --git a/nlp.py b/nlp.py
index f136cb035..bf0b6a6aa 100644
--- a/nlp.py
+++ b/nlp.py
@@ -54,6 +54,7 @@ def isa(self, word, cat):
def __repr__(self):
return ''.format(self.name)
+
E0 = Grammar('E0',
Rules( # Grammar for E_0 [Figure 22.4]
S='NP VP | S Conjunction S',
@@ -196,15 +197,15 @@ def CYK_parse(words, grammar):
P = defaultdict(float)
# Insert lexical rules for each word.
for (i, word) in enumerate(words):
- for (X, p) in grammar.categories[word]: # XXX grammar.categories needs changing, above
+ for (X, p) in grammar.categories[word]: # XXX grammar.categories needs changing, above
P[X, i, 1] = p
# Combine first and second parts of right-hand sides of rules,
# from short to long.
for length in range(2, N+1):
for start in range(N-length+1):
- for len1 in range(1, length): # N.B. the book incorrectly has N instead of length
+ for len1 in range(1, length): # N.B. the book incorrectly has N instead of length
len2 = length - len1
- for (X, Y, Z, p) in grammar.cnf_rules(): # XXX grammar needs this method
+ for (X, Y, Z, p) in grammar.cnf_rules(): # XXX grammar needs this method
P[X, start, length] = max(P[X, start, length],
P[Y, start, len1] * P[Z, start+len1, len2] * p)
return P
@@ -215,17 +216,18 @@ def CYK_parse(words, grammar):
# First entry in list is the base URL, and then following are relative URL pages
examplePagesSet = ["https://en.wikipedia.org/wiki/", "Aesthetics", "Analytic_philosophy",
- "Ancient_Greek", "Aristotle", "Astrology","Atheism", "Baruch_Spinoza",
+ "Ancient_Greek", "Aristotle", "Astrology", "Atheism", "Baruch_Spinoza",
"Belief", "Betrand Russell", "Confucius", "Consciousness",
"Continental Philosophy", "Dialectic", "Eastern_Philosophy",
"Epistemology", "Ethics", "Existentialism", "Friedrich_Nietzsche",
"Idealism", "Immanuel_Kant", "List_of_political_philosophers", "Logic",
"Metaphysics", "Philosophers", "Philosophy", "Philosophy_of_mind", "Physics",
- "Plato", "Political_philosophy", "Pythagoras", "Rationalism","Social_philosophy",
- "Socrates", "Subjectivity", "Theology", "Truth", "Western_philosophy"]
+ "Plato", "Political_philosophy", "Pythagoras", "Rationalism",
+ "Social_philosophy", "Socrates", "Subjectivity", "Theology",
+ "Truth", "Western_philosophy"]
-def loadPageHTML( addressList ):
+def loadPageHTML(addressList):
"""Download HTML page content for every URL address passed as argument"""
contentDict = {}
for addr in addressList:
@@ -236,20 +238,23 @@ def loadPageHTML( addressList ):
contentDict[addr] = html
return contentDict
-def initPages( addressList ):
+
+def initPages(addressList):
"""Create a dictionary of pages from a list of URL addresses"""
pages = {}
for addr in addressList:
pages[addr] = Page(addr)
return pages
-def stripRawHTML( raw_html ):
+
+def stripRawHTML(raw_html):
"""Remove the section of the HTML which contains links to stylesheets etc.,
and remove all other unnessecary HTML"""
# TODO: Strip more out of the raw html
- return re.sub(".*?", "", raw_html, flags=re.DOTALL) # remove section
+ return re.sub(".*?", "", raw_html, flags=re.DOTALL) # remove section
-def determineInlinks( page ):
+
+def determineInlinks(page):
"""Given a set of pages that have their outlinks determined, we can fill
out a page's inlinks by looking through all other page's outlinks"""
inlinks = []
@@ -260,14 +265,16 @@ def determineInlinks( page ):
inlinks.append(addr)
return inlinks
-def findOutlinks( page, handleURLs=None ):
+
+def findOutlinks(page, handleURLs=None):
"""Search a page's HTML content for URL links to other pages"""
urls = re.findall(r'href=[\'"]?([^\'" >]+)', pagesContent[page.address])
if handleURLs:
urls = handleURLs(urls)
return urls
-def onlyWikipediaURLS( urls ):
+
+def onlyWikipediaURLS(urls):
"""Some example HTML page data is from wikipedia. This function converts
relative wikipedia links to full wikipedia URLs"""
wikiURLs = [url for url in urls if url.startswith('/wiki/')]
@@ -277,11 +284,11 @@ def onlyWikipediaURLS( urls ):
# ______________________________________________________________________________
# HITS Helper Functions
-def expand_pages( pages ):
+def expand_pages(pages):
"""From Textbook: adds in every page that links to or is linked from one of
the relevant pages."""
expanded = {}
- for addr,page in pages.items():
+ for addr, page in pages.items():
if addr not in expanded:
expanded[addr] = page
for inlink in page.inlinks:
@@ -292,6 +299,7 @@ def expand_pages( pages ):
expanded[outlink] = pagesIndex[outlink]
return expanded
+
def relevant_pages(query):
"""Relevant pages are pages that contain the query in its entireity.
If a page's content contains the query it is returned by the function."""
@@ -302,16 +310,18 @@ def relevant_pages(query):
relevant[addr] = page
return relevant
-def normalize( pages ):
+
+def normalize(pages):
"""From the pseudocode: Normalize divides each page's score by the sum of
the squares of all pages' scores (separately for both the authority and hubs scores).
"""
- summed_hub = sum(page.hub**2 for _,page in pages.items())
- summed_auth = sum(page.authority**2 for _,page in pages.items())
+ summed_hub = sum(page.hub**2 for _, page in pages.items())
+ summed_auth = sum(page.authority**2 for _, page in pages.items())
for _, page in pages.items():
page.hub /= summed_hub
page.authority /= summed_auth
+
class ConvergenceDetector(object):
"""If the hub and authority values of the pages are no longer changing, we have
reached a convergence and further iterations will have no effect. This detects convergence
@@ -326,16 +336,16 @@ def __call__(self):
def detect(self):
curr_hubs = [page.hub for addr, page in pagesIndex.items()]
curr_auths = [page.authority for addr, page in pagesIndex.items()]
- if self.hub_history == None:
- self.hub_history, self.auth_history = [],[]
+ if self.hub_history is None:
+ self.hub_history, self.auth_history = [], []
else:
- diffsHub = [abs(x-y) for x, y in zip(curr_hubs,self.hub_history[-1])]
- diffsAuth = [abs(x-y) for x, y in zip(curr_auths,self.auth_history[-1])]
+ diffsHub = [abs(x-y) for x, y in zip(curr_hubs, self.hub_history[-1])]
+ diffsAuth = [abs(x-y) for x, y in zip(curr_auths, self.auth_history[-1])]
aveDeltaHub = sum(diffsHub)/float(len(pagesIndex))
aveDeltaAuth = sum(diffsAuth)/float(len(pagesIndex))
- if aveDeltaHub < 0.01 and aveDeltaAuth < 0.01: # may need tweaking
+ if aveDeltaHub < 0.01 and aveDeltaAuth < 0.01: # may need tweaking
return True
- if len(self.hub_history) > 2: # prevent list from getting long
+ if len(self.hub_history) > 2: # prevent list from getting long
del self.hub_history[0]
del self.auth_history[0]
self.hub_history.append([x for x in curr_hubs])
@@ -343,12 +353,13 @@ def detect(self):
return False
-def getInlinks( page ):
+def getInlinks(page):
if not page.inlinks:
page.inlinks = determineInlinks(page)
- return [p for addr, p in pagesIndex.items() if addr in page.inlinks ]
+ return [p for addr, p in pagesIndex.items() if addr in page.inlinks]
-def getOutlinks( page ):
+
+def getOutlinks(page):
if not page.outlinks:
page.outlinks = findOutlinks(page)
return [p for addr, p in pagesIndex.items() if addr in page.outlinks]
@@ -365,20 +376,22 @@ def __init__(self, address, hub=0, authority=0, inlinks=None, outlinks=None):
self.inlinks = inlinks
self.outlinks = outlinks
-pagesContent = {} # maps Page relative or absolute URL/location to page's HTML content
+
+pagesContent = {} # maps Page relative or absolute URL/location to page's HTML content
pagesIndex = {}
-convergence = ConvergenceDetector() # assign function to variable to mimic pseudocode's syntax
+convergence = ConvergenceDetector() # assign function to variable to mimic pseudocode's syntax
+
def HITS(query):
"""The HITS algorithm for computing hubs and authorities with respect to a query."""
- pages = expand_pages(relevant_pages(query)) # in order to 'map' faithfully to pseudocode we
- for p in pages: # won't pass the list of pages as an argument
+ pages = expand_pages(relevant_pages(query)) # in order to 'map' faithfully to pseudocode we
+ for p in pages: # won't pass the list of pages as an argument
p.authority = 1
p.hub = 1
- while True: # repeat until... convergence
+ while True: # repeat until... convergence
for p in pages:
p.authority = sum(x.hub for x in getInlinks(p)) # p.authority ← ∑i Inlinki(p).Hub
- p.hub = sum(x.authority for x in getOutlinks(p)) # p.hub ← ∑i Outlinki(p).Authority
+ p.hub = sum(x.authority for x in getOutlinks(p)) # p.hub ← ∑i Outlinki(p).Authority
normalize(pages)
if convergence():
break
diff --git a/planning.py b/planning.py
index 47eae77da..b92cb6eaa 100644
--- a/planning.py
+++ b/planning.py
@@ -5,6 +5,7 @@
from utils import Expr, expr, first
from logic import FolKB
+
class PDLL:
"""
PDLL used to define a search problem.
@@ -34,6 +35,7 @@ def act(self, action):
raise Exception("Action '{}' pre-conditions not satisfied".format(action))
list_action(self.kb, args)
+
class Action:
"""
Defines an action schema using preconditions and effects.
@@ -112,16 +114,19 @@ def goal_test(kb):
return False
return True
- ## Actions
+ # Actions
+
# Load
- precond_pos = [expr("At(c, a)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"), expr("Airport(a)")]
+ precond_pos = [expr("At(c, a)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"),
+ expr("Airport(a)")]
precond_neg = []
effect_add = [expr("In(c, p)")]
effect_rem = [expr("At(c, a)")]
load = Action(expr("Load(c, p, a)"), [precond_pos, precond_neg], [effect_add, effect_rem])
# Unload
- precond_pos = [expr("In(c, p)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"), expr("Airport(a)")]
+ precond_pos = [expr("In(c, p)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"),
+ expr("Airport(a)")]
precond_neg = []
effect_add = [expr("At(c, a)")]
effect_rem = [expr("In(c, p)")]
@@ -151,31 +156,34 @@ def goal_test(kb):
return False
return True
- ##Actions
- #Remove
+ # Actions
+
+ # Remove
precond_pos = [expr("At(obj, loc)")]
precond_neg = []
effect_add = [expr("At(obj, Ground)")]
effect_rem = [expr("At(obj, loc)")]
remove = Action(expr("Remove(obj, loc)"), [precond_pos, precond_neg], [effect_add, effect_rem])
- #PutOn
+ # PutOn
precond_pos = [expr("Tire(t)"), expr("At(t, Ground)")]
precond_neg = [expr("At(Flat, Axle)")]
effect_add = [expr("At(t, Axle)")]
effect_rem = [expr("At(t, Ground)")]
put_on = Action(expr("PutOn(t, Axle)"), [precond_pos, precond_neg], [effect_add, effect_rem])
- #LeaveOvernight
+ # LeaveOvernight
precond_pos = []
precond_neg = []
effect_add = []
effect_rem = [expr("At(Spare, Ground)"), expr("At(Spare, Axle)"), expr("At(Spare, Trunk)"),
expr("At(Flat, Ground)"), expr("At(Flat, Axle)"), expr("At(Flat, Trunk)")]
- leave_overnight = Action(expr("LeaveOvernight"), [precond_pos, precond_neg], [effect_add, effect_rem])
+ leave_overnight = Action(expr("LeaveOvernight"), [precond_pos, precond_neg],
+ [effect_add, effect_rem])
return PDLL(init, [remove, put_on, leave_overnight], goal_test)
+
def three_block_tower():
init = [expr('On(A, Table)'),
expr('On(B, Table)'),
@@ -193,23 +201,27 @@ def goal_test(kb):
return False
return True
- ## Actions
+ # Actions
+
# Move
- precond_pos = [expr('On(b, x)'), expr('Clear(b)'), expr('Clear(y)'), expr('Block(b)'), expr('Block(y)')]
+ precond_pos = [expr('On(b, x)'), expr('Clear(b)'), expr('Clear(y)'), expr('Block(b)'),
+ expr('Block(y)')]
precond_neg = []
effect_add = [expr('On(b, y)'), expr('Clear(x)')]
effect_rem = [expr('On(b, x)'), expr('Clear(y)')]
move = Action(expr('Move(b, x, y)'), [precond_pos, precond_neg], [effect_add, effect_rem])
-
+
# MoveToTable
precond_pos = [expr('On(b, x)'), expr('Clear(b)'), expr('Block(b)')]
precond_neg = []
effect_add = [expr('On(b, Table)'), expr('Clear(x)')]
effect_rem = [expr('On(b, x)')]
- moveToTable = Action(expr('MoveToTable(b, x)'), [precond_pos, precond_neg], [effect_add, effect_rem])
+ moveToTable = Action(expr('MoveToTable(b, x)'), [precond_pos, precond_neg],
+ [effect_add, effect_rem])
return PDLL(init, [move, moveToTable], goal_test)
+
def have_cake_and_eat_cake_too():
init = [expr('Have(Cake)')]
@@ -220,7 +232,8 @@ def goal_test(kb):
return False
return True
- ##Actions
+ # Actions
+
# Eat cake
precond_pos = [expr('Have(Cake)')]
precond_neg = []
@@ -228,7 +241,7 @@ def goal_test(kb):
effect_rem = [expr('Have(Cake)')]
eat_cake = Action(expr('Eat(Cake)'), [precond_pos, precond_neg], [effect_add, effect_rem])
- #Bake Cake
+ # Bake Cake
precond_pos = []
precond_neg = [expr('Have(Cake)')]
effect_add = [expr('Have(Cake)')]
@@ -247,69 +260,63 @@ class Level():
def __init__(self, poskb, negkb):
self.poskb = poskb
- #Current state
+ # Current state
self.current_state_pos = poskb.clauses
self.current_state_neg = negkb.clauses
- #Current action to current state link
+ # Current action to current state link
self.current_action_links_pos = {}
self.current_action_links_neg = {}
- #Current state to action link
+ # Current state to action link
self.current_state_links_pos = {}
self.current_state_links_neg = {}
- #Current action to next state link
+ # Current action to next state link
self.next_action_links = {}
- #Next state to current action link
+ # Next state to current action link
self.next_state_links_pos = {}
self.next_state_links_neg = {}
self.mutex = []
-
def __call__(self, actions, objects):
self.build(actions, objects)
self.find_mutex()
-
def find_mutex(self):
- #Inconsistent effects
+ # Inconsistent effects
for poseff in self.next_state_links_pos:
- #negeff = Expr('not'+poseff.op, poseff.args)
negeff = poseff
if negeff in self.next_state_links_neg:
for a in self.next_state_links_pos[poseff]:
for b in self.next_state_links_neg[negeff]:
- if set([a,b]) not in self.mutex:
- self.mutex.append(set([a,b]))
+ if set([a, b]) not in self.mutex:
+ self.mutex.append(set([a, b]))
- #Interference
+ # Interference
for posprecond in self.current_state_links_pos:
- #negeff = Expr('not'+posprecond.op, posprecond.args)
negeff = posprecond
if negeff in self.next_state_links_neg:
for a in self.current_state_links_pos[posprecond]:
for b in self.next_state_links_neg[negeff]:
- if set([a,b]) not in self.mutex:
- self.mutex.append(set([a,b]))
+ if set([a, b]) not in self.mutex:
+ self.mutex.append(set([a, b]))
for negprecond in self.current_state_links_neg:
- #poseff = Expr(negprecond.op[3:], negprecond.args)
poseff = negprecond
if poseff in self.next_state_links_pos:
for a in self.next_state_links_pos[poseff]:
for b in self.current_state_links_neg[negprecond]:
- if set([a,b]) not in self.mutex:
- self.mutex.append(set([a,b]))
+ if set([a, b]) not in self.mutex:
+ self.mutex.append(set([a, b]))
- #Competing needs
+ # Competing needs
for posprecond in self.current_state_links_pos:
- #negprecond = Expr('not'+posprecond.op, posprecond.args)
negprecond = posprecond
if negprecond in self.current_state_links_neg:
for a in self.current_state_links_pos[posprecond]:
for b in self.current_state_links_neg[negprecond]:
- if set([a,b]) not in self.mutex:
- self.mutex.append(set([a,b]))
+ if set([a, b]) not in self.mutex:
+ self.mutex.append(set([a, b]))
- #Inconsistent support
+ # Inconsistent support
state_mutex = []
for pair in self.mutex:
next_state_0 = self.next_action_links[list(pair)[0]]
@@ -322,22 +329,22 @@ def find_mutex(self):
self.mutex = self.mutex+state_mutex
-
def build(self, actions, objects):
- #Add persistence actions for positive states
+ # Add persistence actions for positive states
for clause in self.current_state_pos:
self.current_action_links_pos[Expr('Persistence', clause)] = [clause]
self.next_action_links[Expr('Persistence', clause)] = [clause]
self.current_state_links_pos[clause] = [Expr('Persistence', clause)]
self.next_state_links_pos[clause] = [Expr('Persistence', clause)]
- #Add persistence actions for negative states
+ # Add persistence actions for negative states
for clause in self.current_state_neg:
- self.current_action_links_neg[Expr('Persistence', Expr('not'+clause.op, clause.args))] = [clause]
- self.next_action_links[Expr('Persistence', Expr('not'+clause.op, clause.args))] = [clause]
- self.current_state_links_neg[clause] = [Expr('Persistence', Expr('not'+clause.op, clause.args))]
- self.next_state_links_neg[clause] = [Expr('Persistence', Expr('not'+clause.op, clause.args))]
+ not_expr = Expr('not'+clause.op, clause.args)
+ self.current_action_links_neg[Expr('Persistence', not_expr)] = [clause]
+ self.next_action_links[Expr('Persistence', not_expr)] = [clause]
+ self.current_state_links_neg[clause] = [Expr('Persistence', not_expr)]
+ self.next_state_links_neg[clause] = [Expr('Persistence', not_expr)]
for a in actions:
num_args = len(a.args)
@@ -365,7 +372,6 @@ def build(self, actions, objects):
for clause in a.precond_neg:
new_clause = a.substitute(clause, arg)
- #new_clause = Expr('not'+new_clause.op, new_clause.arg)
self.current_action_links_neg[new_action].append(new_clause)
if new_clause in self.current_state_links_neg:
self.current_state_links_neg[new_clause].append(new_action)
@@ -389,9 +395,10 @@ def build(self, actions, objects):
else:
self.next_state_links_neg[new_clause] = [new_action]
-
def perform_actions(self):
- new_kb_pos, new_kb_neg = FolKB(list(set(self.next_state_links_pos.keys()))), FolKB(list(set(self.next_state_links_neg.keys())))
+ new_kb_pos = FolKB(list(set(self.next_state_links_pos.keys())))
+ new_kb_neg = FolKB(list(set(self.next_state_links_neg.keys())))
+
return Level(new_kb_pos, new_kb_neg)
@@ -435,7 +442,12 @@ def __init__(self, pdll, negkb):
self.solution = []
def check_leveloff(self):
- if (set(self.graph.levels[-1].current_state_pos) == set(self.graph.levels[-2].current_state_pos)) and (set(lf.graph.levels[-1].current_state_neg) == set(self.graph.levels[-2].current_state_neg)):
+ first_check = (set(self.graph.levels[-1].current_state_pos) ==
+ set(self.graph.levels[-2].current_state_pos))
+ second_check = (set(self.graph.levels[-1].current_state_neg) ==
+ set(self.graph.levels[-2].current_state_neg))
+
+ if first_check and second_check:
return True
def extract_solution(self, goals_pos, goals_neg, index):
@@ -446,7 +458,7 @@ def extract_solution(self, goals_pos, goals_neg, index):
level = self.graph.levels[index-1]
- #Create all combinations of actions that satisfy the goal
+ # Create all combinations of actions that satisfy the goal
actions = []
for goal in goals_pos:
actions.append(level.next_state_links_pos[goal])
@@ -456,7 +468,7 @@ def extract_solution(self, goals_pos, goals_neg, index):
all_actions = list(itertools.product(*actions))
- #Filter out the action combinations which contain mutexes
+ # Filter out the action combinations which contain mutexes
non_mutex_actions = []
for action_tuple in all_actions:
action_pairs = itertools.combinations(list(set(action_tuple)), 2)
@@ -466,7 +478,7 @@ def extract_solution(self, goals_pos, goals_neg, index):
non_mutex_actions.pop(-1)
break
- #Recursion
+ # Recursion
for action_list in non_mutex_actions:
if [action_list, index] not in self.solution:
self.solution.append([action_list, index])
@@ -488,7 +500,7 @@ def extract_solution(self, goals_pos, goals_neg, index):
else:
self.extract_solution(new_goals_pos, new_goals_neg, index-1)
- #Level-Order multiple solutions
+ # Level-Order multiple solutions
solution = []
for item in self.solution:
if item[1] == -1:
@@ -515,12 +527,14 @@ def spare_tire_graphplan():
pdll = spare_tire()
negkb = FolKB([expr('At(Flat, Trunk)')])
graphplan = GraphPlan(pdll, negkb)
- ##Not sure
+
+ # Not sure
goals_pos = [expr('At(Spare, Axle)'), expr('At(Flat, Ground)')]
goals_neg = []
while True:
- if goal_test(graphplan.graph.levels[-1].poskb, goals_pos) and graphplan.graph.non_mutex_goals(goals_pos+goals_neg, -1):
+ if (goal_test(graphplan.graph.levels[-1].poskb, goals_pos) and
+ graphplan.graph.non_mutex_goals(goals_pos+goals_neg, -1)):
solution = graphplan.extract_solution(goals_pos, goals_neg, -1)
if solution:
return solution
@@ -528,6 +542,7 @@ def spare_tire_graphplan():
if len(graphplan.graph.levels)>=2 and graphplan.check_leveloff():
return None
+
def double_tennis_problem():
init = [expr('At(A, LeftBaseLine)'),
expr('At(B, RightNet)'),
@@ -542,15 +557,16 @@ def goal_test(kb):
return False
return True
- ##actions
- #hit
- precond_pos=[expr("Approaching(Ball, loc)"), expr("At(actor, loc)")]
- precond_neg=[]
- effect_add=[expr("Returned(Ball)")]
+ # Actions
+
+ # Hit
+ precond_pos = [expr("Approaching(Ball,loc)"), expr("At(actor,loc)")]
+ precond_neg = []
+ effect_add = [expr("Returned(Ball)")]
effect_rem = []
hit = Action(expr("Hit(actor, Ball)"), [precond_pos, precond_neg], [effect_add, effect_rem])
- #go
+ # Go
precond_pos = [expr("At(actor, loc)")]
precond_neg = []
effect_add = [expr("At(actor, to)")]
diff --git a/probability.py b/probability.py
index a5699b7f4..e102e4dd8 100644
--- a/probability.py
+++ b/probability.py
@@ -272,6 +272,7 @@ def sample(self, event):
def __repr__(self):
return repr((self.variable, ' '.join(self.parents)))
+
# Burglary example [Figure 14.2]
T, F = True, False
@@ -409,6 +410,7 @@ def all_events(variables, bn, e):
# [Figure 14.12a]: sprinkler network
+
sprinkler = BayesNet([
('Cloudy', '', 0.5),
('Sprinkler', 'Cloudy', {T: 0.10, F: 0.50}),
diff --git a/rl.py b/rl.py
index 77a04f98a..43d860935 100644
--- a/rl.py
+++ b/rl.py
@@ -29,7 +29,7 @@ def T(self, s, a):
def __init__(self, pi, mdp):
self.pi = pi
- self.mdp = PassiveADPAgent.ModelMDP(mdp.init, mdp.actlist,
+ self.mdp = PassiveADPAgent.ModelMDP(mdp.init, mdp.actlist,
mdp.terminals, mdp.gamma, mdp.states)
self.U = {}
self.Nsa = defaultdict(int)
@@ -91,7 +91,7 @@ def __init__(self, pi, mdp, alpha=None):
def __call__(self, percept):
s1, r1 = self.update_state(percept)
- pi, U, Ns, s, a, r = self.pi, self.U, self.Ns, self.s, self.a, self.r
+ pi, U, Ns, s, r = self.pi, self.U, self.Ns, self.s, self.r
alpha, gamma, terminals = self.alpha, self.gamma, self.terminals
if not Ns[s1]:
U[s1] = r1
@@ -153,13 +153,15 @@ def actions_in_state(self, state):
def __call__(self, percept):
s1, r1 = self.update_state(percept)
Q, Nsa, s, a, r = self.Q, self.Nsa, self.s, self.a, self.r
- alpha, gamma, terminals, actions_in_state = self.alpha, self.gamma, self.terminals, self.actions_in_state
+ alpha, gamma, terminals = self.alpha, self.gamma, self.terminals,
+ actions_in_state = self.actions_in_state
+
if s in terminals:
Q[s, None] = r1
if s is not None:
Nsa[s, a] += 1
- Q[s, a] += alpha(Nsa[s, a]) * (r + gamma * max(Q[s1, a1] for a1 in actions_in_state(s1))
- - Q[s, a])
+ Q[s, a] += alpha(Nsa[s, a]) * (r + gamma * max(Q[s1, a1]
+ for a1 in actions_in_state(s1)) - Q[s, a])
if s in terminals:
self.s = self.a = self.r = None
else:
diff --git a/text.py b/text.py
index 65eef28f6..991c764d9 100644
--- a/text.py
+++ b/text.py
@@ -362,7 +362,10 @@ def decode(self, ciphertext):
def score(self, code):
"""Score is product of word scores, unigram scores, and bigram scores.
This can get very small, so we use logs and exp."""
- text = permutation_decode(self.ciphertext, code)
+
+ # TODO: Implement the permutation_decode function
+ text = permutation_decode(self.ciphertext, code) # noqa
+
logP = (sum([log(self.Pwords[word]) for word in words(text)]) +
sum([log(self.P1[c]) for c in text]) +
sum([log(self.P2[b]) for b in bigrams(text)]))
diff --git a/utils.py b/utils.py
index 7a547c67c..ed44f1e9e 100644
--- a/utils.py
+++ b/utils.py
@@ -3,7 +3,6 @@
import bisect
import collections
import collections.abc
-import functools
import operator
import os.path
import random
@@ -59,7 +58,8 @@ def is_in(elt, seq):
"""Similar to (elt in seq), but compares with 'is', not '=='."""
return any(x is elt for x in seq)
-def mode(data):
+
+def mode(data):
"""Return the most common data item. If there are ties, return any one of them."""
[(item, count)] = collections.Counter(data).most_common(1)
return item
@@ -67,6 +67,7 @@ def mode(data):
# ______________________________________________________________________________
# argmin and argmax
+
identity = lambda x: x
argmin = min
@@ -90,7 +91,6 @@ def shuffled(iterable):
return items
-
# ______________________________________________________________________________
# Statistical and mathematical functions
@@ -167,7 +167,6 @@ def vector_add(a, b):
return tuple(map(operator.add, a, b))
-
def scalar_vector_product(X, Y):
"""Return vector as a product of a scalar and a vector"""
return [X * y for y in Y]
@@ -259,6 +258,7 @@ def step(x):
"""Return activation value of x with sign function"""
return 1 if x >= 0 else 0
+
try: # math.isclose was added in Python 3.5; but we might be in 3.4
from math import isclose
except ImportError:
@@ -361,21 +361,50 @@ def __init__(self, op, *args):
self.args = args
# Operator overloads
- def __neg__(self): return Expr('-', self)
- def __pos__(self): return Expr('+', self)
- def __invert__(self): return Expr('~', self)
- def __add__(self, rhs): return Expr('+', self, rhs)
- def __sub__(self, rhs): return Expr('-', self, rhs)
- def __mul__(self, rhs): return Expr('*', self, rhs)
- def __pow__(self, rhs): return Expr('**',self, rhs)
- def __mod__(self, rhs): return Expr('%', self, rhs)
- def __and__(self, rhs): return Expr('&', self, rhs)
- def __xor__(self, rhs): return Expr('^', self, rhs)
- def __rshift__(self, rhs): return Expr('>>', self, rhs)
- def __lshift__(self, rhs): return Expr('<<', self, rhs)
- def __truediv__(self, rhs): return Expr('/', self, rhs)
- def __floordiv__(self, rhs): return Expr('//', self, rhs)
- def __matmul__(self, rhs): return Expr('@', self, rhs)
+ def __neg__(self):
+ return Expr('-', self)
+
+ def __pos__(self):
+ return Expr('+', self)
+
+ def __invert__(self):
+ return Expr('~', self)
+
+ def __add__(self, rhs):
+ return Expr('+', self, rhs)
+
+ def __sub__(self, rhs):
+ return Expr('-', self, rhs)
+
+ def __mul__(self, rhs):
+ return Expr('*', self, rhs)
+
+ def __pow__(self, rhs):
+ return Expr('**', self, rhs)
+
+ def __mod__(self, rhs):
+ return Expr('%', self, rhs)
+
+ def __and__(self, rhs):
+ return Expr('&', self, rhs)
+
+ def __xor__(self, rhs):
+ return Expr('^', self, rhs)
+
+ def __rshift__(self, rhs):
+ return Expr('>>', self, rhs)
+
+ def __lshift__(self, rhs):
+ return Expr('<<', self, rhs)
+
+ def __truediv__(self, rhs):
+ return Expr('/', self, rhs)
+
+ def __floordiv__(self, rhs):
+ return Expr('//', self, rhs)
+
+ def __matmul__(self, rhs):
+ return Expr('@', self, rhs)
def __or__(self, rhs):
"""Allow both P | Q, and P |'==>'| Q."""
@@ -385,20 +414,47 @@ def __or__(self, rhs):
return PartialExpr(rhs, self)
# Reverse operator overloads
- def __radd__(self, lhs): return Expr('+', lhs, self)
- def __rsub__(self, lhs): return Expr('-', lhs, self)
- def __rmul__(self, lhs): return Expr('*', lhs, self)
- def __rdiv__(self, lhs): return Expr('/', lhs, self)
- def __rpow__(self, lhs): return Expr('**', lhs, self)
- def __rmod__(self, lhs): return Expr('%', lhs, self)
- def __rand__(self, lhs): return Expr('&', lhs, self)
- def __rxor__(self, lhs): return Expr('^', lhs, self)
- def __ror__(self, lhs): return Expr('|', lhs, self)
- def __rrshift__(self, lhs): return Expr('>>', lhs, self)
- def __rlshift__(self, lhs): return Expr('<<', lhs, self)
- def __rtruediv__(self, lhs): return Expr('/', lhs, self)
- def __rfloordiv__(self, lhs): return Expr('//', lhs, self)
- def __rmatmul__(self, lhs): return Expr('@', lhs, self)
+ def __radd__(self, lhs):
+ return Expr('+', lhs, self)
+
+ def __rsub__(self, lhs):
+ return Expr('-', lhs, self)
+
+ def __rmul__(self, lhs):
+ return Expr('*', lhs, self)
+
+ def __rdiv__(self, lhs):
+ return Expr('/', lhs, self)
+
+ def __rpow__(self, lhs):
+ return Expr('**', lhs, self)
+
+ def __rmod__(self, lhs):
+ return Expr('%', lhs, self)
+
+ def __rand__(self, lhs):
+ return Expr('&', lhs, self)
+
+ def __rxor__(self, lhs):
+ return Expr('^', lhs, self)
+
+ def __ror__(self, lhs):
+ return Expr('|', lhs, self)
+
+ def __rrshift__(self, lhs):
+ return Expr('>>', lhs, self)
+
+ def __rlshift__(self, lhs):
+ return Expr('<<', lhs, self)
+
+ def __rtruediv__(self, lhs):
+ return Expr('/', lhs, self)
+
+ def __rfloordiv__(self, lhs):
+ return Expr('//', lhs, self)
+
+ def __rmatmul__(self, lhs):
+ return Expr('@', lhs, self)
def __call__(self, *args):
"Call: if 'f' is a Symbol, then f(0) == Expr('f', 0)."
@@ -430,6 +486,7 @@ def __repr__(self):
# An 'Expression' is either an Expr or a Number.
# Symbol is not an explicit type; it is any Expr with 0 args.
+
Number = (int, float, complex)
Expression = (Expr, Number)
@@ -464,9 +521,14 @@ def arity(expression):
class PartialExpr:
"""Given 'P |'==>'| Q, first form PartialExpr('==>', P), then combine with Q."""
- def __init__(self, op, lhs): self.op, self.lhs = op, lhs
- def __or__(self, rhs): return Expr(self.op, self.lhs, rhs)
- def __repr__(self): return "PartialExpr('{}', {})".format(self.op, self.lhs)
+ def __init__(self, op, lhs):
+ self.op, self.lhs = op, lhs
+
+ def __or__(self, rhs):
+ return Expr(self.op, self.lhs, rhs)
+
+ def __repr__(self):
+ return "PartialExpr('{}', {})".format(self.op, self.lhs)
def expr(x):
@@ -482,6 +544,7 @@ def expr(x):
else:
return x
+
infix_ops = '==> <== <=>'.split()
@@ -614,5 +677,6 @@ class Bool(int):
"""Just like `bool`, except values display as 'T' and 'F' instead of 'True' and 'False'"""
__str__ = __repr__ = lambda self: 'T' if self else 'F'
+
T = Bool(True)
F = Bool(False)