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) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy