From 219637d92a5710cc78fa44b23c0ed4d1b9b36b6d Mon Sep 17 00:00:00 2001 From: Lucas Moura Date: Wed, 22 Mar 2017 15:28:35 -0300 Subject: [PATCH 1/3] Exclude test files from flake8 check --- .flake8 | 1 + 1 file changed, 1 insertion(+) 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 From 58ece5d2095ed0919b8b6607b319a157f8faca04 Mon Sep 17 00:00:00 2001 From: Lucas Moura Date: Wed, 22 Mar 2017 15:29:18 -0300 Subject: [PATCH 2/3] Fix flake8 for main files --- agents.py | 74 +++++++++++++-------------- canvas.py | 10 ++-- csp.py | 4 +- games.py | 12 +++-- ipyviews.py | 6 ++- learning.py | 49 ++++++++++-------- logic.py | 11 ++-- mdp.py | 8 +-- nlp.py | 79 ++++++++++++++++------------ planning.py | 133 ++++++++++++++++++++++++++--------------------- probability.py | 2 + rl.py | 12 +++-- text.py | 5 +- utils.py | 136 ++++++++++++++++++++++++++++++++++++------------- 14 files changed, 329 insertions(+), 212 deletions(-) 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 1e97d7780..83957a730 100644 --- a/csp.py +++ b/csp.py @@ -377,6 +377,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: ') @@ -547,7 +548,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 981a557c2..6775df675 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)) @@ -35,6 +36,7 @@ def manhattan_distance(predictions, targets): def mean_boolean_error(predictions, targets): return mean(int(p != t) for p, t in zip(predictions, targets)) + def hamming_distance(predictions, targets): return sum(p != t for p, t in zip(predictions, targets)) @@ -154,15 +156,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] @@ -376,7 +378,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?""" @@ -868,6 +870,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") @@ -891,6 +894,7 @@ def RestaurantDataSet(examples=None): attrnames='Alternate Bar Fri/Sat Hungry Patrons Price ' + 'Raining Reservation Type WaitEstimate Wait') + restaurant = RestaurantDataSet() @@ -900,28 +904,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 9054cdfc7..78b18a755 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_]) @@ -862,6 +866,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 17028e4c6..2d8c478f6 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)')] @@ -237,6 +250,7 @@ def goal_test(kb): return PDLL(init, [eat_cake, bake_cake], goal_test) + class Level(): """ Contains the state of the planning problem @@ -246,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]] @@ -321,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) @@ -364,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) @@ -388,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) @@ -434,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): @@ -445,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]) @@ -455,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) @@ -465,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]) @@ -487,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: @@ -514,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 @@ -527,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)'), @@ -541,16 +557,17 @@ def goal_test(kb): return False return True - ##actions - #hit + # 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 - precond_pos = [ expr("At(actor,loc)")] + # Go + precond_pos = [expr("At(actor,loc)")] precond_neg = [] effect_add = [expr("At(actor,to)")] effect_rem = [expr("At(actor,loc)")] diff --git a/probability.py b/probability.py index 1d7992e6d..aa9360669 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 855e89aaf..780826116 100644 --- a/text.py +++ b/text.py @@ -348,7 +348,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 73dd63d63..4f2a6b115 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: @@ -367,21 +367,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.""" @@ -391,20 +420,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)." @@ -436,6 +492,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) @@ -470,9 +527,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): @@ -488,6 +550,7 @@ def expr(x): else: return x + infix_ops = '==> <== <=>'.split() @@ -620,5 +683,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) From 287eb72b9505c67662d09254ca7d6acc78cef28b Mon Sep 17 00:00:00 2001 From: Lucas Moura Date: Wed, 22 Mar 2017 15:29:34 -0300 Subject: [PATCH 3/3] Add flake8 check to build --- .travis.yml | 1 + 1 file changed, 1 insertion(+) 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 .