-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
isa3
committed
Feb 28, 2020
1 parent
fde3340
commit 66efe64
Showing
1 changed file
with
334 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,334 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": { | ||
"collapsed": true | ||
}, | ||
"source": [ | ||
"# Homework 1" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## 1.1\n", | ||
"\n", | ||
"Introspection would not be a good way for researchers to gain insight into human cognitive processes. While the earliest psychologists were limited to introspection in terms of their techniques to study the mind, modern psychologists and neuroscientists have a much more diverse array of technology and resources to examine human cognition. Introspection has its merits; when a cognitive process is going on, the closest person to understanding it should be the person in whose mind it is occurring. However, all too often this isn’t the case, since the process might be in the person’s subconscious or simply be inaccessible to the subject themselves.\n", | ||
"\n", | ||
"A neuroscientist or psychologist, on the other hand, can evaluate the process much more empirically and with decreased bias from an external, test-driven perspective. Most of the advancements in neuroscience, such as neuroplasticity and the connection of some processes to their respective parts of the brain, have been found through such methods, not through introspection. However, to go further a new method entirely might be required. We are still no closer to understanding why humans experience the world in the way we do, despite all of our efforts." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## 1.2\n", | ||
"\n", | ||
"I created a TSP class which accepts two parameters besides self. The first is the initial state/ordering of locations amongst which the traveling salesperson wants to travel optimally. The second is an UndirectedGraph containing the distances between all cities in that list.\n", | ||
"actions() returns a list of 2-tuples. Each tuple contains one of pair of indexes into the state, such that all pairs are represented besides the ones where the two indexes are equal.\n", | ||
"result() returns a copy of the parameter state where the values at the indexes passed by the 2-tuple have been swapped.\n", | ||
"value() evaluates the passed list of cities and returns the distance traveled if the salesman were to travel between all of them and back to the first in order." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 11, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"\"\"\"\n", | ||
"This module implements local search on a traveling salesperson problem.\n", | ||
"Written by Ian Adams for CS344 at Calvin University, Spring 2020.\n", | ||
"\"\"\"\n", | ||
"import sys\n", | ||
"sys.path.append('/home/isa3/cs344-code/tools/aima')\n", | ||
"import math\n", | ||
"import time\n", | ||
"from search import UndirectedGraph, Problem, hill_climbing, simulated_annealing, exp_schedule\n", | ||
"\n", | ||
"\n", | ||
"class TSP(Problem):\n", | ||
" \"\"\"\n", | ||
" State: list of city names (strings)\n", | ||
" Move: swap two names in the list\n", | ||
" \"\"\"\n", | ||
" \n", | ||
" def __init__(self, initial, graph):\n", | ||
" \"\"\"\n", | ||
" Initializes a traveling salesperson problem, based on an initial ordered list\n", | ||
" of cities and an UndirectedGraph of all distances between said cities.\n", | ||
" \"\"\"\n", | ||
" self.initial = initial\n", | ||
" self.graph = graph\n", | ||
" \n", | ||
" def actions(self, state):\n", | ||
" \"\"\"\n", | ||
" Given a state, return a list of 2-tuples of all combinations of indexes into\n", | ||
" the state where the values in the tuple are not the same.\n", | ||
" \"\"\"\n", | ||
" actionlist = []\n", | ||
" for i in range(len(state)):\n", | ||
" for j in range(len(state)):\n", | ||
" if i != j and (i,j) not in actionlist and (j,i) not in actionlist:\n", | ||
" actionlist.append((i,j))\n", | ||
" return actionlist\n", | ||
" \n", | ||
" def result(self, state, action):\n", | ||
" \"\"\"\n", | ||
" Given a state and a tuple of indexes, return a copy of the state with the \n", | ||
" cities at said indexes swapped.\n", | ||
" \"\"\"\n", | ||
" newState = state[:]\n", | ||
" newState[action[0]], newState[action[1]] = state[action[1]], state[action[0]]\n", | ||
" return newState\n", | ||
" \n", | ||
" def value(self, state):\n", | ||
" \"\"\"\n", | ||
" Value a state by the sum of the distances between the cities in its list,\n", | ||
" including from the final index to the first index.\n", | ||
" \"\"\"\n", | ||
" length = 0\n", | ||
" for i in range(len(state)):\n", | ||
" length -= self.graph.get(state[i], state[(i+1) % len(state)])\n", | ||
" return length" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"My sample city domain is the cities of Romania, and pseudo-randomly selected values between approximately 0 and 250 miles for the distances between them. The hill-climbing method consistently performs worse than the simulated annealing method; typically the reordered path is 800-900 miles long for simulated annealing, and 950 miles exactly for hill climbing, given an initial ordering that is alphabetical. Either way, the initial path length is 2003 miles, so this is a great improvement on the original ordering regardless." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 12, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Initial x: ['arad', 'bucharest', 'craiova', 'dobreta', 'eforie', 'fagaras', 'hirsova', 'iasi', 'lugoj', 'oradea', 'pitesti', 'rimnicuvilcea', 'sibiu', 'timisoara', 'urziceni', 'vaslui', 'zerind']\tmiles: 2003\t\ttime: 0.000 seconds\n", | ||
"Hill-climbing solution x: ['arad', 'craiova', 'sibiu', 'vaslui', 'dobreta', 'fagaras', 'hirsova', 'oradea', 'lugoj', 'iasi', 'pitesti', 'rimnicuvilcea', 'timisoara', 'urziceni', 'bucharest', 'eforie', 'zerind']\tmiles: 950\t\ttime: 0.018 seconds\n", | ||
"Simulated annealing solution x: ['vaslui', 'sibiu', 'craiova', 'fagaras', 'dobreta', 'rimnicuvilcea', 'timisoara', 'bucharest', 'urziceni', 'arad', 'iasi', 'pitesti', 'zerind', 'lugoj', 'eforie', 'hirsova', 'oradea']\tmiles: 848\t\ttime: 0.956 seconds\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"initial = [\"arad\", \"bucharest\", \"craiova\", \"dobreta\", \"eforie\", \"fagaras\", \"hirsova\", \"iasi\", \"lugoj\", \"oradea\", \n", | ||
" \"pitesti\", \"rimnicuvilcea\", \"sibiu\", \"timisoara\", \"urziceni\", \"vaslui\", \"zerind\"]\n", | ||
"\n", | ||
"romania = UndirectedGraph(dict(\n", | ||
" arad=dict(bucharest=110, craiova=52, dobreta=94, eforie=253, fagaras=23, hirsova=139, iasi=53, lugoj=201, oradea=99, pitesti=189, rimnicuvilcea=47, sibiu=140, timisoara=118, urziceni=100, vaslui=82, zerind=75),\n", | ||
" bucharest=dict(craiova=225, dobreta=183, eforie=45, fagaras=97, hirsova=201, iasi=8, lugoj=79, oradea=157, pitesti=104, rimnicuvilcea=83, sibiu=205, timisoara=43, urziceni=42, vaslui=166, zerind=130),\n", | ||
" craiova=dict(dobreta=93, eforie=180, fagaras=44, hirsova=100, iasi=85, lugoj=143, oradea=137, pitesti=194, rimnicuvilcea=140, sibiu=12, timisoara=155, urziceni=87, vaslui=110, zerind=37),\n", | ||
" dobreta=dict(eforie=134, fagaras=25, hirsova=173, iasi=200, lugoj=181, oradea=104, pitesti=70, rimnicuvilcea=42, sibiu=98, timisoara=164, urziceni=115, vaslui=96, zerind=101),\n", | ||
" eforie=dict(fagaras=99, hirsova=57, iasi=82, lugoj=69, oradea=119, pitesti=140, rimnicuvilcea=19, sibiu=199, timisoara=84, urziceni=105, vaslui=201, zerind=48),\n", | ||
" fagaras=dict(hirsova=101, iasi=42, lugoj=169, oradea=190, pitesti=12, rimnicuvilcea=207, sibiu=225, timisoara=153, urziceni=104, vaslui=94, zerind=159),\n", | ||
" hirsova=dict(iasi=201, lugoj=112, oradea=7, pitesti=94, rimnicuvilcea=142, sibiu=307, timisoara=79, urziceni=62, vaslui=124, zerind=148),\n", | ||
" iasi=dict(lugoj=87, oradea=97, pitesti=25, rimnicuvilcea=68, sibiu=41, timisoara=166, urziceni=152, vaslui=95, zerind=14),\n", | ||
" lugoj=dict(oradea=145, pitesti=174, rimnicuvilcea=130, sibiu=129, timisoara=187, urziceni=204, vaslui=207, zerind=85),\n", | ||
" oradea=dict(pitesti=82, rimnicuvilcea=115, sibiu=79, timisoara=190, urziceni=35, vaslui=70, zerind=101),\n", | ||
" pitesti=dict(rimnicuvilcea=22, sibiu=106, timisoara=56, urziceni=155, vaslui=94, zerind=49),\n", | ||
" rimnicuvilcea=dict(sibiu=186, timisoara=40, urziceni=101, vaslui=154, zerind=98),\n", | ||
" sibiu=dict(timisoara=89, urziceni=98, vaslui=85, zerind=163),\n", | ||
" timisoara=dict(urziceni=43, vaslui=94, zerind=204),\n", | ||
" urziceni=dict(vaslui=142, zerind=92), \n", | ||
" vaslui=dict(zerind=169)))\n", | ||
"\n", | ||
"time1 = time.time()\n", | ||
"p = TSP(initial, romania)\n", | ||
"time2 = time.time()\n", | ||
"print('Initial x: ' + str(p.initial)\n", | ||
" + '\\tmiles: ' + str(p.value(initial) * -1)\n", | ||
" + \"\\t\\ttime: %0.3f seconds\" % (time2 - time1)\n", | ||
" )\n", | ||
"\n", | ||
"# Solve the problem using hill-climbing.\n", | ||
"time1 = time.time()\n", | ||
"hill_solution = hill_climbing(p)\n", | ||
"time2 = time.time()\n", | ||
"print('Hill-climbing solution x: ' + str(hill_solution)\n", | ||
" + '\\tmiles: ' + str(p.value(hill_solution) * -1)\n", | ||
" + \"\\t\\ttime: %0.3f seconds\" % (time2 - time1)\n", | ||
" )\n", | ||
"\n", | ||
"# Solve the problem using simulated annealing.\n", | ||
"time1 = time.time()\n", | ||
"annealing_solution = simulated_annealing(\n", | ||
" p,\n", | ||
" exp_schedule(k=20, lam=0.005, limit=1000)\n", | ||
")\n", | ||
"time2 = time.time()\n", | ||
"print('Simulated annealing solution x: ' + str(annealing_solution)\n", | ||
" + '\\tmiles: ' + str(p.value(annealing_solution) * -1)\n", | ||
" + \"\\t\\ttime: %0.3f seconds\" % (time2 - time1)\n", | ||
" )" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"This problem was very helpful in terms of understanding what Problem required from a subclass, and was additionally very helpful in terms of understanding the requirements for a local search algorithm better. I spent a long time going down the wrong rabbit hole at first, but even that was helpful in terms of helping me understand what the Problem class, hill_climbing, and simulated_annealing needed me to define." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"## 1.3\n", | ||
"\n", | ||
"I created a CourseScheduling() function which accepts five parameters: variables, a list of the course names, professors, a list of the professors teaching courses, rooms, a list of rooms in which classes can be taught, times, a list of times at which classes can be taught, and class_assignments, a dictionary mapping each course name to the professor teaching it.\n", | ||
"\n", | ||
"From there, I created a domains dictionary mapping each course in variables containing 3-tuples of all possible combinations of professor, time, and classroom. I chose the courses to be variables because while each professor can teach multiple courses, multiple courses can happen at the same time, and multiple courses can happen in the same room, each course occurs only once. Therefore, it makes more sense to assign the other options as values for a specific course, rather than the other way around.\n", | ||
"\n", | ||
"I additionally created a neighbors dictionary mapping each course to the courses with which it had to meet the constraints; namely, all the courses that weren't the key course.\n", | ||
"\n", | ||
"Finally, I created a constraint function ensuring that the current values assigned to the variables met the constraints that first, the assigned value for both courses had to have the correct professor according to class_assignment, and second that if the assigned values for both courses had the same time, then the assigned values must have different professors and rooms to still return true. If the courses didn't have the same time, it would return true after the first condition was met." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 13, | ||
"metadata": { | ||
"pycharm": { | ||
"is_executing": false | ||
} | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"\"\"\"\n", | ||
"This module implements a class scheduling CSP.\n", | ||
"Written by Ian Adams for CS344 at Calvin University, Spring 2020.\n", | ||
"\"\"\"\n", | ||
"\n", | ||
"from csp import CSP, min_conflicts, backtracking_search, AC3, mrv, forward_checking\n", | ||
"\n", | ||
"def ClassSchedule(variables, professors, rooms, times, class_assignments):\n", | ||
" domains = {}\n", | ||
" neighbors = {}\n", | ||
"\n", | ||
" for var in variables:\n", | ||
" domains[var] = []\n", | ||
" neighbors[var] = []\n", | ||
" for prof in professors:\n", | ||
" for time in times:\n", | ||
" for room in rooms:\n", | ||
" domains[var].append([prof, time, room])\n", | ||
"\n", | ||
"\n", | ||
" for A in variables:\n", | ||
" for B in variables:\n", | ||
" if A != B:\n", | ||
" if B not in neighbors[A]:\n", | ||
" neighbors[A].append(B)\n", | ||
" if A not in neighbors[B]:\n", | ||
" neighbors[B].append(A)\n", | ||
"\n", | ||
" def schedule_constraint(A, a, B, b):\n", | ||
" if class_assignments[A] != a[0] or class_assignments[B] != b[0]:\n", | ||
" return False\n", | ||
" if a[1] == b[1]:\n", | ||
" return (a[2] != b[2] and a[0] != b[0])\n", | ||
" return True\n", | ||
"\n", | ||
" return CSP(variables, domains, neighbors, schedule_constraint)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"My sample course scheduling domain had seven courses, four professors, two rooms, and four times. Three professors were assigned to two courses each, and one professor was assigned to one.\n", | ||
"The constraint satisfaction problem specified worked perfectly. I used backtracking_search and it quickly found a valid assignment every single time." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 14, | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Schedule:\n", | ||
"\n", | ||
"112 taught by adams at mwf8 in nh256\n", | ||
"214 taught by adams at mwf9 in nh256\n", | ||
"344 taught by vanderlinden at mwf8 in sb382\n", | ||
"212 taught by plantinga at mwf9 in sb382\n", | ||
"384 taught by schuurman at mwf10 in nh256\n", | ||
"300 taught by schuurman at mwf11 in nh256\n", | ||
"108 taught by vanderlinden at mwf10 in sb382\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"variables = [\"108\", \"112\", \"212\", \"214\", \"300\", \"344\", \"384\"]\n", | ||
"professors = [\"schuurman\", \"adams\", \"vanderlinden\", \"plantinga\"]\n", | ||
"rooms = [\"nh256\", \"sb382\"]\n", | ||
"times = [\"mwf8\", \"mwf9\", \"mwf10\", \"mwf11\"]\n", | ||
"class_assignments = {\n", | ||
" \"108\" : \"vanderlinden\",\n", | ||
" \"112\" : \"adams\",\n", | ||
" \"212\" : \"plantinga\",\n", | ||
" \"214\" : \"adams\",\n", | ||
" \"300\" : \"schuurman\",\n", | ||
" \"344\" : \"vanderlinden\",\n", | ||
" \"384\" : \"schuurman\"\n", | ||
"}\n", | ||
"schedule = ClassSchedule(variables, professors, rooms, times, class_assignments)\n", | ||
"result = backtracking_search(schedule, select_unassigned_variable=mrv, inference=forward_checking)\n", | ||
"if schedule.goal_test(schedule.infer_assignment()):\n", | ||
" print(\"Schedule:\\n\")\n", | ||
" for (var, val) in result.items():\n", | ||
" print(var, \"taught by\", val[0], \"at\", val[1], \"in\", val[2])\n", | ||
"else:\n", | ||
" print(\"failed...\")\n", | ||
" schedule.display(schedule.infer_assignment())" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"This problem was equally as helpful as the previous one; while challenging, it very much helped me understand what I needed to specify for a CSP, and I now have a much better idea of how one would specify a CSP with an arbitrary number of options that one could use as variables." | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.7.4" | ||
}, | ||
"pycharm": { | ||
"stem_cell": { | ||
"cell_type": "raw", | ||
"metadata": { | ||
"collapsed": false | ||
}, | ||
"source": [] | ||
} | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 1 | ||
} |