cosc604 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cosc604-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019-2026 Andreas Henschel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
cosc604-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: cosc604
3
+ Version: 0.1.0
4
+ Summary: Teaching implementations of search algorithms (BFS, DFS, UCS, A*) from Russell & Norvig's Artificial Intelligence: A Modern Approach, developed for COSC604.
5
+ Author-email: Andreas Henschel <andreas.henschel@ku.ac.ae>
6
+ License: MIT
7
+ Keywords: artificial-intelligence,search,aima,education,russell-norvig
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Education
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Topic :: Education
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: numpy
18
+ Requires-Dist: networkx
19
+ Provides-Extra: plot
20
+ Requires-Dist: matplotlib; extra == "plot"
21
+ Dynamic: license-file
22
+
23
+ # cosc604
24
+
25
+ Teaching code for **COSC604 – Techniques in Artificial Intelligence**,
26
+ developed alongside Assignment 1 (instructor: Andreas Henschel,
27
+ andreas.henschel@ku.ac.ae). It implements the generic search infrastructure
28
+ from Russell & Norvig, *Artificial Intelligence: A Modern Approach* (Ch. 3):
29
+ fringes/queues, the search-tree `Node`, `graph_search`/`tree_search`, and the
30
+ standard strategies built on top of them (BFS, DFS, uniform-cost, A*).
31
+
32
+ `Assignment1.ipynb` walks through the same code interactively, with the
33
+ routing-problem and tile-puzzle exercises described there. The modules under
34
+ `src/cosc604/` are the packaged, importable version of that code.
35
+
36
+ ## Package layout
37
+
38
+ | Module | Contents |
39
+ | --- | --- |
40
+ | `cosc604.searches` | `Fringe`, `FIFO`, `LIFO`, `PriorityQueue`, `Node`, `graph_search`, `tree_search`, and the convenience wrappers `breadth_first_graph_search`, `depth_first_graph_search`, `depth_first_tree_search`, `astar_graph_search`, `uniform_cost_search`. |
41
+ | `cosc604.priority_queue_demo` | Standalone examples of how `PriorityQueue`'s priority function `f` shapes ordering (digit-sum, "VIP title" count). Run with `python -m cosc604.priority_queue_demo`. |
42
+ | `cosc604.graph_problem` | `GraphProblem` — the routing problem from the lecture slides, built on `networkx`. Includes the toy example and the Romania map. |
43
+ | `cosc604.puzzle_problem` | `PuzzleProblem` / `PuzzleState` — the sliding tile puzzle (8-puzzle for `size=3`). `PuzzleState.successors`, `__hash__` and `__eq__` are left as an exercise — implement them to make the puzzle searchable. |
44
+
45
+ ## Installation
46
+
47
+ From PyPI:
48
+
49
+ ```bash
50
+ pip install cosc604
51
+ ```
52
+
53
+ Or, from this directory, as an editable install:
54
+
55
+ ```bash
56
+ pip install -e .
57
+ ```
58
+
59
+ This pulls in `numpy` and `networkx`. To also plot the Romania graph
60
+ (`graph_problem.draw_romania()`), install the optional `plot` extra:
61
+
62
+ ```bash
63
+ pip install "cosc604[plot]"
64
+ ```
65
+
66
+ ## Quickstart
67
+
68
+ ```python
69
+ from cosc604 import GraphProblem, uniform_cost_search
70
+
71
+ connections = [('S', 'A', 5), ('S', 'B', 3), ('S', 'C', 1),
72
+ ('A', 'G', 1), ('B', 'G', 2), ('C', 'G', 17)]
73
+ toy = GraphProblem('S', 'G', connections, directed=True)
74
+
75
+ solution = uniform_cost_search(toy)
76
+ print([(node.state, node.action) for node in solution.getPath()])
77
+ ```
78
+
79
+ ## Assignment
80
+
81
+ Assignment 1 asks you to:
82
+
83
+ 1. Add a `LIFO` fringe and complete the generic search algorithms (both done
84
+ here) — read through `searches.py` to understand how `graph_search` and
85
+ `tree_search` use a `Fringe` to implement each strategy.
86
+ 2. Implement `PuzzleState.successors`, `__hash__` and `__eq__` in
87
+ `puzzle_problem.py` so `PuzzleProblem` can be solved with the same search
88
+ algorithms used for the routing problem, and reproduce the tile-puzzle
89
+ results shown in the slides (`tileslides.png`).
90
+
91
+ ## License
92
+
93
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,71 @@
1
+ # cosc604
2
+
3
+ Teaching code for **COSC604 – Techniques in Artificial Intelligence**,
4
+ developed alongside Assignment 1 (instructor: Andreas Henschel,
5
+ andreas.henschel@ku.ac.ae). It implements the generic search infrastructure
6
+ from Russell & Norvig, *Artificial Intelligence: A Modern Approach* (Ch. 3):
7
+ fringes/queues, the search-tree `Node`, `graph_search`/`tree_search`, and the
8
+ standard strategies built on top of them (BFS, DFS, uniform-cost, A*).
9
+
10
+ `Assignment1.ipynb` walks through the same code interactively, with the
11
+ routing-problem and tile-puzzle exercises described there. The modules under
12
+ `src/cosc604/` are the packaged, importable version of that code.
13
+
14
+ ## Package layout
15
+
16
+ | Module | Contents |
17
+ | --- | --- |
18
+ | `cosc604.searches` | `Fringe`, `FIFO`, `LIFO`, `PriorityQueue`, `Node`, `graph_search`, `tree_search`, and the convenience wrappers `breadth_first_graph_search`, `depth_first_graph_search`, `depth_first_tree_search`, `astar_graph_search`, `uniform_cost_search`. |
19
+ | `cosc604.priority_queue_demo` | Standalone examples of how `PriorityQueue`'s priority function `f` shapes ordering (digit-sum, "VIP title" count). Run with `python -m cosc604.priority_queue_demo`. |
20
+ | `cosc604.graph_problem` | `GraphProblem` — the routing problem from the lecture slides, built on `networkx`. Includes the toy example and the Romania map. |
21
+ | `cosc604.puzzle_problem` | `PuzzleProblem` / `PuzzleState` — the sliding tile puzzle (8-puzzle for `size=3`). `PuzzleState.successors`, `__hash__` and `__eq__` are left as an exercise — implement them to make the puzzle searchable. |
22
+
23
+ ## Installation
24
+
25
+ From PyPI:
26
+
27
+ ```bash
28
+ pip install cosc604
29
+ ```
30
+
31
+ Or, from this directory, as an editable install:
32
+
33
+ ```bash
34
+ pip install -e .
35
+ ```
36
+
37
+ This pulls in `numpy` and `networkx`. To also plot the Romania graph
38
+ (`graph_problem.draw_romania()`), install the optional `plot` extra:
39
+
40
+ ```bash
41
+ pip install "cosc604[plot]"
42
+ ```
43
+
44
+ ## Quickstart
45
+
46
+ ```python
47
+ from cosc604 import GraphProblem, uniform_cost_search
48
+
49
+ connections = [('S', 'A', 5), ('S', 'B', 3), ('S', 'C', 1),
50
+ ('A', 'G', 1), ('B', 'G', 2), ('C', 'G', 17)]
51
+ toy = GraphProblem('S', 'G', connections, directed=True)
52
+
53
+ solution = uniform_cost_search(toy)
54
+ print([(node.state, node.action) for node in solution.getPath()])
55
+ ```
56
+
57
+ ## Assignment
58
+
59
+ Assignment 1 asks you to:
60
+
61
+ 1. Add a `LIFO` fringe and complete the generic search algorithms (both done
62
+ here) — read through `searches.py` to understand how `graph_search` and
63
+ `tree_search` use a `Fringe` to implement each strategy.
64
+ 2. Implement `PuzzleState.successors`, `__hash__` and `__eq__` in
65
+ `puzzle_problem.py` so `PuzzleProblem` can be solved with the same search
66
+ algorithms used for the routing problem, and reproduce the tile-puzzle
67
+ results shown in the slides (`tileslides.png`).
68
+
69
+ ## License
70
+
71
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cosc604"
7
+ version = "0.1.0"
8
+ description = "Teaching implementations of search algorithms (BFS, DFS, UCS, A*) from Russell & Norvig's Artificial Intelligence: A Modern Approach, developed for COSC604."
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Andreas Henschel", email = "andreas.henschel@ku.ac.ae" },
14
+ ]
15
+ keywords = ["artificial-intelligence", "search", "aima", "education", "russell-norvig"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Education",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
22
+ "Topic :: Education",
23
+ ]
24
+ dependencies = [
25
+ "numpy",
26
+ "networkx",
27
+ ]
28
+
29
+ [project.optional-dependencies]
30
+ plot = ["matplotlib"]
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ """cosc604: teaching implementations of the search algorithms from
2
+ Russell & Norvig, *Artificial Intelligence: A Modern Approach* (Ch. 3),
3
+ developed for COSC604 - Techniques in Artificial Intelligence.
4
+ """
5
+ from .searches import (
6
+ Fringe, FIFO, LIFO, PriorityQueue, Node,
7
+ graph_search, tree_search,
8
+ breadth_first_graph_search, depth_first_graph_search,
9
+ depth_first_tree_search, astar_graph_search, uniform_cost_search,
10
+ )
11
+ from .graph_problem import GraphProblem
12
+ from .puzzle_problem import PuzzleProblem, PuzzleState
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ __all__ = [
17
+ "Fringe", "FIFO", "LIFO", "PriorityQueue", "Node",
18
+ "graph_search", "tree_search",
19
+ "breadth_first_graph_search", "depth_first_graph_search",
20
+ "depth_first_tree_search", "astar_graph_search", "uniform_cost_search",
21
+ "GraphProblem", "PuzzleProblem", "PuzzleState",
22
+ ]
@@ -0,0 +1,69 @@
1
+ """The Routing problem from the textbook: states are cities, actions move
2
+ along weighted edges of a graph. GraphProblem plugs into the generic
3
+ searches.py algorithms via the successors/goal_test interface.
4
+
5
+ Run directly with: python -m cosc604.graph_problem
6
+ """
7
+ import networkx as nx
8
+
9
+ from .searches import uniform_cost_search
10
+
11
+
12
+ class GraphProblem:
13
+ def __init__(self, initial, goal, connections, locations=None, directed=False):
14
+ self.initial = initial
15
+ self.goal = goal
16
+ self.locations = locations
17
+ self.graph = nx.DiGraph() if directed else nx.Graph()
18
+ for cityA, cityB, distance in connections:
19
+ self.graph.add_edge(cityA, cityB, cost=distance)
20
+
21
+ def successors(self, state):
22
+ # Exactly as defined in Lecture slides
23
+ return [("go to %s" % city, connection['cost'], city)
24
+ for city, connection in self.graph[state].items()]
25
+
26
+ def goal_test(self, state):
27
+ return state == self.goal
28
+
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # Warm up: the little toy example from the slides
32
+ # ---------------------------------------------------------------------------
33
+
34
+ toyConnections = [('S', 'A', 5), ('S', 'B', 3), ('S', 'C', 1),
35
+ ('A', 'G', 1), ('B', 'G', 2), ('C', 'G', 17)]
36
+ toy = GraphProblem('S', 'G', toyConnections, directed=True)
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Romania: the classic bigger example. Try different initial/goal states.
41
+ # ---------------------------------------------------------------------------
42
+
43
+ connections = [('A', 'S', 140), ('A', 'Z', 75), ('A', 'T', 118), ('C', 'P', 138), ('C', 'R', 146), ('C', 'D', 120),
44
+ ('B', 'P', 101), ('B', 'U', 85), ('B', 'G', 90), ('B', 'F', 211), ('E', 'H', 86), ('D', 'M', 75),
45
+ ('F', 'S', 99), ('I', 'V', 92), ('I', 'N', 87), ('H', 'U', 98), ('L', 'M', 70), ('L', 'T', 111),
46
+ ('O', 'S', 151), ('O', 'Z', 71), ('P', 'R', 97), ('R', 'S', 80), ('U', 'V', 142)]
47
+
48
+ locations = {'A': (91, 492), 'C': (253, 288), 'B': (400, 327), 'E': (562, 293), 'D': (165, 299), 'G': (375, 270), 'F': (305, 449),
49
+ 'I': (473, 506), 'H': (534, 350), 'M': (168, 339), 'L': (165, 379), 'O': (131, 571), 'N': (406, 537), 'P': (320, 368),
50
+ 'S': (207, 457), 'R': (233, 410), 'U': (456, 350), 'T': (94, 410), 'V': (509, 444), 'Z': (108, 531)}
51
+
52
+ romania = GraphProblem('A', 'B', connections, locations=locations)
53
+
54
+
55
+ def draw_romania():
56
+ """Plot the Romania graph (needs matplotlib; run inside a notebook or
57
+ with an interactive backend to actually see the figure)."""
58
+ import pylab
59
+ pylab.clf()
60
+ nx.draw(romania.graph, romania.locations, with_labels=True)
61
+ pylab.show()
62
+
63
+
64
+ if __name__ == "__main__":
65
+ print("Toy successors of C:", toy.successors('C'))
66
+
67
+ sol = uniform_cost_search(toy)
68
+ path = [(node.state, node.action) for node in sol.getPath()]
69
+ print("Uniform-cost solution S -> G:", path)
@@ -0,0 +1,62 @@
1
+ """Standalone examples of how searches.PriorityQueue orders items.
2
+
3
+ The PriorityQueue is a Fringe that takes a function `f` at construction time.
4
+ `f` maps an item (later: a search Node) to a priority; the queue always pops
5
+ the item with the lowest priority first. These two examples use plain
6
+ integers and strings (not Nodes) so you can see the ordering mechanics in
7
+ isolation, before PriorityQueue gets reused as the fringe for
8
+ uniform-cost / A* search in searches.py.
9
+
10
+ Run directly with: python -m cosc604.priority_queue_demo
11
+ """
12
+ from .searches import PriorityQueue
13
+
14
+
15
+ def digitSum(nr):
16
+ """Priority = sum of the item's digits (smaller sum -> popped first)."""
17
+ ds = 0
18
+ while nr > 0:
19
+ ds += nr % 10
20
+ nr = nr // 10
21
+ return ds
22
+
23
+
24
+ def countImportance(title):
25
+ """The importance is the number of V's in the title, with more V's being
26
+ more important. The negation is used so that the priority queue will
27
+ order elements with more V's first, since the priority queue orders by
28
+ lowest value first."""
29
+ return -title.split()[0].count('V')
30
+
31
+
32
+ def demo_digit_sum():
33
+ print("=== PriorityQueue ordered by digit sum ===")
34
+ pq = PriorityQueue(digitSum)
35
+
36
+ # Example from class: the priority queue will order elements based on
37
+ # the sum of their digits
38
+ for element in [432, 52, 99, 12]:
39
+ pq.push(element)
40
+
41
+ while not pq.empty():
42
+ print(pq.pop())
43
+ print("Remaining PQ:", pq.fringe)
44
+
45
+
46
+ def demo_vip_titles():
47
+ print("=== PriorityQueue ordered by number of V's in the title ===")
48
+ pq = PriorityQueue(countImportance)
49
+ pq.push("Normal dude")
50
+ pq.push("VVVIP Elon Musk")
51
+ pq.push("Voldemort")
52
+ pq.push("VVIP Bill Gates")
53
+
54
+ while not pq.empty():
55
+ print(pq.pop())
56
+ print("Remaining PQ:", pq.fringe)
57
+
58
+
59
+ if __name__ == "__main__":
60
+ demo_digit_sum()
61
+ print()
62
+ demo_vip_titles()
@@ -0,0 +1,66 @@
1
+ """The sliding tile puzzle problem (e.g. the 8-puzzle for size=3).
2
+
3
+ PuzzleState.successors/__hash__/__eq__ are left as stubs for you to
4
+ implement -- see the comments inside each method. Once PuzzleState is
5
+ complete, PuzzleProblem plugs straight into the generic search algorithms
6
+ in searches.py, the same way GraphProblem does.
7
+ """
8
+ import random
9
+
10
+ import numpy as np
11
+
12
+
13
+ class PuzzleState:
14
+ def __init__(self, matrix=None, goal=False, init=False, size=3):
15
+ self.size = size
16
+ if not matrix is None: # 0 represents empty spot
17
+ self.matrix = matrix
18
+ else: # defining init or goal state
19
+ permutation = np.array(range(size * size))
20
+ if init:
21
+ random.shuffle(permutation)
22
+ self.matrix = permutation.reshape((size, size))
23
+
24
+ def successors(self):
25
+ pass
26
+ # return a list of successors
27
+ # each successor is a tuple of
28
+ # 1. a string, describing the action,
29
+ # 2. action cost (here 1),
30
+ # 3. and the new state
31
+
32
+ def __hash__(self):
33
+ pass
34
+ # return a hash code, if you have a matrix, you can uncomment this:
35
+ # return hash(tuple(self.matrix.flatten()))
36
+
37
+ def __eq__(self, other):
38
+ pass
39
+ # this function defines equality between objects, if you have a matrix, you can uncomment this:
40
+ # return np.alltrue(other.matrix == self.matrix)
41
+
42
+ def __repr__(self):
43
+ return str(self.matrix)
44
+
45
+
46
+ class PuzzleProblem:
47
+ def __init__(self, size=3): # size 3 means 3x3 field
48
+ self.size = size
49
+ self.initial = PuzzleState(init=True, size=size) # init state is shuffled
50
+ self.goal = PuzzleState(goal=True, size=size) # goal state is unshuffled
51
+
52
+ def successors(self, state):
53
+ # this assumes that PuzzleState does the actual job of finding the successors
54
+ return state.successors()
55
+
56
+ def goal_test(self, state):
57
+ return state == self.goal
58
+
59
+
60
+ if __name__ == "__main__":
61
+ puzzle = PuzzleProblem(size=3)
62
+ print("Initial state:\n", puzzle.initial)
63
+ print("Goal state:\n", puzzle.goal)
64
+ # Once PuzzleState is implemented, e.g.:
65
+ # from searches import breadth_first_graph_search
66
+ # sol = breadth_first_graph_search(puzzle)
@@ -0,0 +1,142 @@
1
+ """Generic search infrastructure: fringes (queues), the Node class, and the
2
+ graph/tree search algorithms, plus convenience wrappers for the standard
3
+ search strategies (BFS, DFS, uniform-cost, A*).
4
+
5
+ This module is problem-agnostic: it only relies on a `problem` object that
6
+ exposes `initial`, `goal_test(state)` and `successors(state)`. See
7
+ graph_problem.py and puzzle_problem.py for concrete problems.
8
+ """
9
+ import bisect
10
+
11
+
12
+ # ---------------------------------------------------------------------------
13
+ # Fringes (the frontier / open list used by search)
14
+ # ---------------------------------------------------------------------------
15
+
16
+ class Fringe:
17
+ def __init__(self):
18
+ self.fringe = []
19
+
20
+ def empty(self):
21
+ return len(self.fringe) == 0
22
+
23
+
24
+ class FIFO(Fringe):
25
+ """First-in-first-out queue -> breadth-first search."""
26
+ def push(self, item):
27
+ self.fringe.append(item)
28
+
29
+ def pop(self):
30
+ return self.fringe.pop(0)
31
+
32
+
33
+ class LIFO(Fringe):
34
+ """Last-in-first-out queue (a stack) -> depth-first search."""
35
+ def push(self, item):
36
+ self.fringe.append(item)
37
+
38
+ def pop(self):
39
+ return self.fringe.pop()
40
+
41
+
42
+ class PriorityQueue(Fringe):
43
+ """Orders items by ascending priority, where priority is computed by
44
+ the function `f` passed at construction time. See priority_queue_demo.py
45
+ for standalone examples of how `f` shapes the ordering."""
46
+ def __init__(self, f):
47
+ self.f = f
48
+ super().__init__()
49
+
50
+ def push(self, item):
51
+ # apply the function that provides the priority for the current item
52
+ # later, items will be nodes, and priorities will be backward-, forward cost or both
53
+ priority = self.f(item)
54
+ bisect.insort(self.fringe, (priority, item))
55
+
56
+ def pop(self):
57
+ return self.fringe.pop(0)[1]
58
+
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # Search tree node
62
+ # ---------------------------------------------------------------------------
63
+
64
+ class Node:
65
+ def __init__(self, state=None, parent=None, action=None, path_cost=0):
66
+ self.state = state
67
+ self.parent = parent
68
+ self.action = action
69
+ self.path_cost = path_cost
70
+
71
+ def getPath(self):
72
+ """getting the path of parents up to the root"""
73
+ currentNode = self
74
+ path = [self]
75
+ while currentNode.parent: # stops when parent is None, ie root
76
+ path.append(currentNode.parent)
77
+ currentNode = currentNode.parent
78
+ path.reverse() # from root to this node
79
+ return path
80
+
81
+ def expand(self, problem):
82
+ successors = problem.successors(self.state)
83
+ return [Node(newState, self, action, self.path_cost + cost)
84
+ for (action, cost, newState) in successors]
85
+
86
+
87
+ # ---------------------------------------------------------------------------
88
+ # Search algorithms
89
+ # ---------------------------------------------------------------------------
90
+
91
+ def graph_search(problem, fringe):
92
+ """Search through the successors of a problem to find a goal.
93
+ The argument fringe should be an empty queue.
94
+ If two paths reach a state, only use the best one. [Fig. 3.18]"""
95
+ closed = set() # can store hashable objects, thats why we need to define a hash code for states
96
+ fringe.push(Node(problem.initial))
97
+ while not fringe.empty():
98
+ node = fringe.pop()
99
+ if problem.goal_test(node.state):
100
+ return node
101
+ if node.state not in closed:
102
+ closed.add(node.state)
103
+ successors = node.expand(problem)
104
+ for snode in successors:
105
+ fringe.push(snode)
106
+
107
+
108
+ def tree_search(problem, fringe):
109
+ """Search through the successors of a problem to find a goal.
110
+ The argument fringe should be an empty queue."""
111
+ fringe.push(Node(problem.initial))
112
+ while not fringe.empty():
113
+ node = fringe.pop()
114
+ if problem.goal_test(node.state):
115
+ return node
116
+ successors = node.expand(problem)
117
+ for snode in successors:
118
+ fringe.push(snode)
119
+
120
+
121
+ # ---------------------------------------------------------------------------
122
+ # Convenience functions for the standard search strategies
123
+ # ---------------------------------------------------------------------------
124
+
125
+ def breadth_first_graph_search(problem):
126
+ return graph_search(problem, FIFO())
127
+
128
+
129
+ def depth_first_graph_search(problem):
130
+ return graph_search(problem, LIFO())
131
+
132
+
133
+ def depth_first_tree_search(problem):
134
+ return tree_search(problem, LIFO())
135
+
136
+
137
+ def astar_graph_search(problem, f):
138
+ return graph_search(problem, PriorityQueue(f))
139
+
140
+
141
+ def uniform_cost_search(problem):
142
+ return graph_search(problem, PriorityQueue(lambda node: node.path_cost))
@@ -0,0 +1,93 @@
1
+ Metadata-Version: 2.4
2
+ Name: cosc604
3
+ Version: 0.1.0
4
+ Summary: Teaching implementations of search algorithms (BFS, DFS, UCS, A*) from Russell & Norvig's Artificial Intelligence: A Modern Approach, developed for COSC604.
5
+ Author-email: Andreas Henschel <andreas.henschel@ku.ac.ae>
6
+ License: MIT
7
+ Keywords: artificial-intelligence,search,aima,education,russell-norvig
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Education
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Topic :: Education
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: numpy
18
+ Requires-Dist: networkx
19
+ Provides-Extra: plot
20
+ Requires-Dist: matplotlib; extra == "plot"
21
+ Dynamic: license-file
22
+
23
+ # cosc604
24
+
25
+ Teaching code for **COSC604 – Techniques in Artificial Intelligence**,
26
+ developed alongside Assignment 1 (instructor: Andreas Henschel,
27
+ andreas.henschel@ku.ac.ae). It implements the generic search infrastructure
28
+ from Russell & Norvig, *Artificial Intelligence: A Modern Approach* (Ch. 3):
29
+ fringes/queues, the search-tree `Node`, `graph_search`/`tree_search`, and the
30
+ standard strategies built on top of them (BFS, DFS, uniform-cost, A*).
31
+
32
+ `Assignment1.ipynb` walks through the same code interactively, with the
33
+ routing-problem and tile-puzzle exercises described there. The modules under
34
+ `src/cosc604/` are the packaged, importable version of that code.
35
+
36
+ ## Package layout
37
+
38
+ | Module | Contents |
39
+ | --- | --- |
40
+ | `cosc604.searches` | `Fringe`, `FIFO`, `LIFO`, `PriorityQueue`, `Node`, `graph_search`, `tree_search`, and the convenience wrappers `breadth_first_graph_search`, `depth_first_graph_search`, `depth_first_tree_search`, `astar_graph_search`, `uniform_cost_search`. |
41
+ | `cosc604.priority_queue_demo` | Standalone examples of how `PriorityQueue`'s priority function `f` shapes ordering (digit-sum, "VIP title" count). Run with `python -m cosc604.priority_queue_demo`. |
42
+ | `cosc604.graph_problem` | `GraphProblem` — the routing problem from the lecture slides, built on `networkx`. Includes the toy example and the Romania map. |
43
+ | `cosc604.puzzle_problem` | `PuzzleProblem` / `PuzzleState` — the sliding tile puzzle (8-puzzle for `size=3`). `PuzzleState.successors`, `__hash__` and `__eq__` are left as an exercise — implement them to make the puzzle searchable. |
44
+
45
+ ## Installation
46
+
47
+ From PyPI:
48
+
49
+ ```bash
50
+ pip install cosc604
51
+ ```
52
+
53
+ Or, from this directory, as an editable install:
54
+
55
+ ```bash
56
+ pip install -e .
57
+ ```
58
+
59
+ This pulls in `numpy` and `networkx`. To also plot the Romania graph
60
+ (`graph_problem.draw_romania()`), install the optional `plot` extra:
61
+
62
+ ```bash
63
+ pip install "cosc604[plot]"
64
+ ```
65
+
66
+ ## Quickstart
67
+
68
+ ```python
69
+ from cosc604 import GraphProblem, uniform_cost_search
70
+
71
+ connections = [('S', 'A', 5), ('S', 'B', 3), ('S', 'C', 1),
72
+ ('A', 'G', 1), ('B', 'G', 2), ('C', 'G', 17)]
73
+ toy = GraphProblem('S', 'G', connections, directed=True)
74
+
75
+ solution = uniform_cost_search(toy)
76
+ print([(node.state, node.action) for node in solution.getPath()])
77
+ ```
78
+
79
+ ## Assignment
80
+
81
+ Assignment 1 asks you to:
82
+
83
+ 1. Add a `LIFO` fringe and complete the generic search algorithms (both done
84
+ here) — read through `searches.py` to understand how `graph_search` and
85
+ `tree_search` use a `Fringe` to implement each strategy.
86
+ 2. Implement `PuzzleState.successors`, `__hash__` and `__eq__` in
87
+ `puzzle_problem.py` so `PuzzleProblem` can be solved with the same search
88
+ algorithms used for the routing problem, and reproduce the tile-puzzle
89
+ results shown in the slides (`tileslides.png`).
90
+
91
+ ## License
92
+
93
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/cosc604/__init__.py
5
+ src/cosc604/graph_problem.py
6
+ src/cosc604/priority_queue_demo.py
7
+ src/cosc604/puzzle_problem.py
8
+ src/cosc604/searches.py
9
+ src/cosc604.egg-info/PKG-INFO
10
+ src/cosc604.egg-info/SOURCES.txt
11
+ src/cosc604.egg-info/dependency_links.txt
12
+ src/cosc604.egg-info/requires.txt
13
+ src/cosc604.egg-info/top_level.txt
@@ -0,0 +1,5 @@
1
+ numpy
2
+ networkx
3
+
4
+ [plot]
5
+ matplotlib
@@ -0,0 +1 @@
1
+ cosc604