{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "“Homework_11_Scheduling_with_Dependencies.ipynb”的副本", "provenance": [], "collapsed_sections": [] }, "kernelspec": {...

1 answer below »
Q3&4


{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "“Homework_11_Scheduling_with_Dependencies.ipynb”的副本", "provenance": [], "collapsed_sections": [] }, "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.3" }, "test_info": { "id": "4adb3d5055da02e7ae8251ca99f4acfa901ab256" } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "IIqniH2KkacL" }, "source": [ "Before you turn this problem in, make sure everything runs as expected. First, **restart the kernel** (in the menubar, select Kernel$\\rightarrow$Restart) and then **run all cells** (in the menubar, select Cell$\\rightarrow$Run All).\n", "\n", "Make sure you fill in any place that says `YOUR CODE HERE` or \"YOUR ANSWER HERE\", as well as your name and collaborators below:" ] }, { "cell_type": "code", "metadata": { "id": "vZ2QllLpkacL" }, "source": [ "NAME = \"Yuxuan Guo\"\n", "COLLABORATORS = \"\"" ], "execution_count": 1, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "JhnyL4GOkacL" }, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": { "deletable": false, "editable": false, "nbgrader": { "checksum": "767690caa09bf309165134adec898cd2", "grade": false, "grade_id": "cell-fc6b5241f439f72f", "locked": true, "schema_version": 1, "solution": false }, "id": "cU91rlxQkacM" }, "source": [ "# Homework 11: Scheduling with Dependencies\n", "\n", "Copyright Luca de Alfaro, 2019-20. \n", "License: [CC-BY-NC-ND](https://creativecommons.org/licenses/by-nc-nd/4.0/)." ] }, { "cell_type": "markdown", "metadata": { "deletable": false, "editable": false, "nbgrader": { "checksum": "93460885ba39aaf770bbcf46acb31f4a", "grade": false, "grade_id": "cell-a13bd1c44a595199", "locked": true, "schema_version": 1, "solution": false }, "id": "ica_ZmxrkacM" }, "source": [ "## Submission\n", "\n", "[Please submit to this Google Form](https://docs.google.com/forms/d/e/1FAIpQLSdVfcA_LZdtjJoLKBSkyHwbvxXvow-CX4pQEMmW3UVm7X_JrA/viewform?usp=sf_link).\n", "\n", "Deadline: Friday December 4, 11pm (check on Canvas for updated information)." ] }, { "cell_type": "markdown", "metadata": { "deletable": false, "editable": false, "nbgrader": { "checksum": "90a74a81d08e34489013a1201369ad0a", "grade": false, "grade_id": "cell-dcea49d1e015f68d", "locked": true, "schema_version": 1, "solution": false }, "id": "Kny6AHUmkacM" }, "source": [ "## Test Format\n", "\n", "This test contains 4 questions, for a total of 90 points. " ] }, { "cell_type": "markdown", "metadata": { "deletable": false, "editable": false, "nbgrader": { "checksum": "637cf59cf5df5ba15cbc30a73b035287", "grade": false, "grade_id": "cell-7783f146ca3d6158", "locked": true, "schema_version": 1, "solution": false }, "id": "FKxK7Pb0kacM" }, "source": [ "Assume you have to prepare Pasta Carbonara. My version of the recipe goes like this: \n", "\n", "> Dice onions and pancetta, and fry in a mix of olive oil and butter, slowly. Separately, put in a bowl as many eggs as there are dinner guests; you can either put in the bowls the yolks only, or you can add a few whites if you wish. Beat the eggs. \n", "> Bring water to a boil, and when it boils, salt it. Put the pasta in (I like Penne Rigate). When cooked, colander the water away, and quickly unite in the bowl the beaten eggs, the pasta, and the pancetta. Mix well and serve immediately. \n", "\n", "If you have to invite people over, you could do this recipe sequentially, and first worry about cooking the pasta: warming the water, putting the pasta in, then colandering it. Then you could worry about cooking the pancetta and onions. When that's done, you can start to beat the eggs. Finally, you could unite everything. Technically, that would work, but there would be two problems. The first is that, of course, the pasta would be rather cold by the time it would be served, a capital sin (pasta must be served immediately after it is cooked). Secondly, even if you rehash the order so that you first cook the pancetta, then beat the eggs, then cook the pasta, then technically this works -- but it would take you well over one hour to have everything ready. You want to do things in parallel, cooking the pancetta while heating up the water for the pasta, and so forth. You want to discover what are the things that need to be done one after the other, and what are the things that can be done in parallel, and in which order to do everything. \n", "\n", "Great cooking, by the way, is much about the perfect timing, not only the perfect preparation. You have to have the various preparations ready at the same time, to unite them just right. We will worry about timing in the second part of this chapter; first, we worry about what we can do and in which order.\n", "\n", "As an aside for those of you who are more interested in compiling code than in cooking, the problem of how to compile C or C++ code is very similar. A makefile defines dependencies between tasks: you have to have compiled
Answered Same DayDec 03, 2021

Answer To: { "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name":...

Swapnil answered on Dec 05 2021
161 Votes
Assign/.ipynb_checkpoints/Question File-checkpoint.ipynb{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "IIqniH2KkacL"
},
"source": [
"Before you turn this problem in, make sure everything runs as expected. First, **restart the kernel** (in the menubar, select Kernel$\\rightarrow$Restart) and then **run all cells** (in the menubar, select Cell$\\rightarrow$Run All).\n",
"\n",
"Make sure you fill in any place that says `YOUR CODE HERE` or \"YOUR ANSWER HERE\", as well as your name and collaborators below:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"id": "vZ2QllLpkacL"
},
"outputs": [],
"source": [
"NAME = \"Yuxuan Guo\"\n",
"COLLABORATORS = \"\""
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "JhnyL4GOkacL"
},
"source": [
"---"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "cU91rlxQkacM",
"nbgrader": {
"checksum": "767690caa09bf309165134adec898cd2",
"grade": false,
"grade_id": "cell-fc6b5241f439f72f",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"# Homework 11: Scheduling with Dependencies\n",
"\n",
"Copyright Luca de Alfaro, 2019-20. \n",
"License: [CC-BY-NC-ND](https://creativecommons.org/licenses/by-nc-nd/4.0/)."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "ica_ZmxrkacM",
"nbgrader": {
"checksum": "93460885ba39aaf770bbcf46acb31f4a",
"grade": false,
"grade_id": "cell-a13bd1c44a595199",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Submission\n",
"\n",
"[Please submit to this Google Form](https://docs.google.com/forms/d/e/1FAIpQLSdVfcA_LZdtjJoLKBSkyHwbvxXvow-CX4pQEMmW3UVm7X_JrA/viewform?usp=sf_link).\n",
"\n",
"Deadline: Friday December 4, 11pm (check on Canvas for updated information)."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "Kny6AHUmkacM",
"nbgrader": {
"checksum": "90a74a81d08e34489013a1201369ad0a",
"grade": false,
"grade_id": "cell-dcea49d1e015f68d",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Test Format\n",
"\n",
"This test contains 4 questions, for a total of 90 points. "
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "FKxK7Pb0kacM",
"nbgrader": {
"checksum": "637cf59cf5df5ba15cbc30a73b035287",
"grade": false,
"grade_id": "cell-7783f146ca3d6158",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Assume you have to prepare Pasta Carbonara. My version of the recipe goes like this: \n",
"\n",
"> Dice onions and pancetta, and fry in a mix of olive oil and butter, slowly. Separately, put in a bowl as many eggs as there are dinner guests; you can either put in the bowls the yolks only, or you can add a few whites if you wish. Beat the eggs. \n",
"> Bring water to a boil, and when it boils, salt it. Put the pasta in (I like Penne Rigate). When cooked, colander the water away, and quickly unite in the bowl the beaten eggs, the pasta, and the pancetta. Mix well and serve immediately. \n",
"\n",
"If you have to invite people over, you could do this recipe sequentially, and first worry about cooking the pasta: warming the water, putting the pasta in, then colandering it. Then you could worry about cooking the pancetta and onions. When that's done, you can start to beat the eggs. Finally, you could unite everything. Technically, that would work, but there would be two problems. The first is that, of course, the pasta would be rather cold by the time it would be served, a capital sin (pasta must be served immediately after it is cooked). Secondly, even if you rehash the order so that you first cook the pancetta, then beat the eggs, then cook the pasta, then technically this works -- but it would take you well over one hour to have everything ready. You want to do things in parallel, cooking the pancetta while heating up the water for the pasta, and so forth. You want to discover what are the things that need to be done one after the other, and what are the things that can be done in parallel, and in which order to do everything. \n",
"\n",
"Great cooking, by the way, is much about the perfect timing, not only the perfect preparation. You have to have the various preparations ready at the same time, to unite them just right. We will worry about timing in the second part of this chapter; first, we worry about what we can do and in which order.\n",
"\n",
"As an aside for those of you who are more interested in compiling code than in cooking, the problem of how to compile C or C++ code is very similar. A makefile defines dependencies between tasks: you have to have compiled pathlib.c before you can link the result together with something else. The task of the make program is to figure out how to parallelize the compilation, so that independent tasks can happen in different processes (possibly on different CPU cores), while respecting the precedence constraints between tasks. We will mention this application in some of the exercises of the chapter. \n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "ZDoY3oEIkacM",
"nbgrader": {
"checksum": "c746ef312885249b098a4a4fbf9f588c",
"grade": false,
"grade_id": "cell-fe45e7ce127db0d4",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Scheduling dependent tasks"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "cIxy_GYJkacM",
"nbgrader": {
"checksum": "13ab8e8184a5ce432629a168358998e9",
"grade": false,
"grade_id": "cell-ac839becba0a561a",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"We first disregard the problem of cooking (or compiling) time, and ask about the order in which we should be doing the tasks. We want to create a _Scheduler_ object, that can tell us what to do at the same time. What operations should this object support? \n",
"\n",
"* **add_task:** we should be able to add a task, along with the task dependencies. \n",
"* **reset:** indicating that we are about to run the sequences of tasks again.\n",
"* **available_tasks:** this property should return the set of things that we can do in parallel. \n",
"* **mark_completed:** used to notify the scheduler that we have completed a task. This should return the set of new tasks that we can do due to this task being completed; we can do these tasks in parallel alongside with the others that we are already doing. \n",
"* **all_done:** returns True/False according to whether we have completed all tasks. \n",
"\n",
"Choosing these operations is perhaps the most important step in the design of the scheduler. The operations need to have a simple, clear definition, and be useful in a concrete implementation of the service which will run the tasks. Of the above operations, they are all uncontroversial, except for the choice of behavior of _completed_. In theory, there is no need for _completed_ to return the set of _new_ tasks that can now be undertaken. If one remembers the set of tasks $T_1$ one can a do before a task $t \\in T_1$ is completed, and marks $t$ as completed, one can simply ask the scheduler for the set of tasks $T_2$ that can now be done, and add those in $T_{21t} = T_2 \\setminus (\\{t\\} \\cup T_1)$ for execution. However, we guess (as we have not yet written the task execution engine) that being told this set of tasks directly will simplify the design of the task execution engine. "
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "yk-RsN-TkacM",
"nbgrader": {
"checksum": "79aecd3e08b0a5afe9f42889882e0bda",
"grade": false,
"grade_id": "cell-307dbeebce53643f",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Our scheduler class will be implemented in similar fashion to our graph class, with tasks corresponding to graph vertices, and dependencies represented as edges.\n",
"The difference is that here, given a vertex (that is, a task) $v$, it will be useful to be able to access both:\n",
"\n",
"* the _predecessors_ of $v$, that is, the tasks $u$ that are declared as prerequisites of $v$, and \n",
"* the _successors_ of $v$, that is, the tasks $u$ such that $v$ was declared as a prerequisite for $u$. \n",
"\n",
"When we add a task, we would have to initialize its set of successors and predecessors to empty. This is somewhat tedious, and so we resort to a defaultdict, which is a special type of dictionary such that, if the mapping for a key has not been defined, it returns a default value; in our case, an empty set. [You can read more about defaultdict and related types here](https://docs.python.org/3.7/library/collections.html#collections.defaultdict). \n",
"\n",
"Our first implementation of the class is as follows. We let you complete the `available_tasks` and `mark_completed` methods. \n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"deletable": false,
"editable": false,
"id": "_yCelWBbkacM",
"nbgrader": {
"checksum": "a3122da716c36329d70b6398881d9f6c",
"grade": false,
"grade_id": "cell-c1f62e6e5511e278",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"from collections import defaultdict\n",
"import networkx as nx # Library for displaying graphs.\n",
"import matplotlib.pyplot as plt\n",
"\n",
"class DependencyScheduler(object):\n",
"\n",
" def __init__(self):\n",
" self.tasks = set()\n",
" # The successors of a task are the tasks that depend on it, and can\n",
" # only be done once the task is completed.\n",
" self.successors = defaultdict(set)\n",
" # The predecessors of a task have to be done before the task.\n",
" self.predecessors = defaultdict(set)\n",
" self.completed_tasks = set() # completed tasks\n",
"\n",
" def add_task(self, t, dependencies):\n",
" \"\"\"Adds a task t with given dependencies.\"\"\"\n",
" # Makes sure we know about all tasks mentioned.\n",
" assert t not in self.tasks or len(self.predecessors[t]) == 0, \"The task was already present.\"\n",
" self.tasks.add(t)\n",
" self.tasks.update(dependencies)\n",
" # The predecessors are the tasks that need to be done before.\n",
" self.predecessors[t] = set(dependencies)\n",
" # The new task is a successor of its dependencies.\n",
" for u in dependencies:\n",
" self.successors[u].add(t)\n",
"\n",
" def reset(self):\n",
" self.completed_tasks = set()\n",
"\n",
" @property\n",
" def done(self):\n",
" return self.completed_tasks == self.tasks\n",
"\n",
"\n",
" def show(self):\n",
" \"\"\"We use the nx graph to display the graph.\"\"\"\n",
" g = nx.DiGraph()\n",
" g.add_nodes_from(self.tasks)\n",
" g.add_edges_from([(u, v) for u in self.tasks for v in self.successors[u]])\n",
" node_colors = ''.join([('g' if v in self.completed_tasks else 'r')\n",
" for v in self.tasks])\n",
" nx.draw(g, with_labels=True, node_color=node_colors)\n",
" plt.show()\n",
"\n",
" @property\n",
" def uncompleted(self):\n",
" \"\"\"Returns the tasks that have not been completed.\n",
" This is a property, so you can say scheduler.uncompleted rather than\n",
" scheduler.uncompleted()\"\"\"\n",
" return self.tasks - self.completed_tasks\n",
"\n",
" def _check(self):\n",
" \"\"\"We check that if t is a successor of u, then u is a predecessor\n",
" of t.\"\"\"\n",
" for u in self.tasks:\n",
" for t in self.successors[u]:\n",
" assert u in self.predecessors[t]\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "WxGmfJPCkacM",
"nbgrader": {
"checksum": "f7194367628d4362f83e04121a68a6e4",
"grade": false,
"grade_id": "cell-1d75cf1333aa8c76",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Question 1: implement `available_tasks` and `mark_completed`. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"deletable": false,
"id": "W3lz1UGRkacM",
"nbgrader": {
"checksum": "f830262ac9c7f13eb8f07e85e0e1ac11",
"grade": false,
"grade_id": "cell-5c9e5f503f616888",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"### Implementation of `available_tasks` and `mark_completed`.\n",
"\n",
"def scheduler_available_tasks(self):\n",
" \"\"\"Returns the set of tasks that can be done in parallel.\n",
" A task can be done if all its predecessors have been completed.\n",
" And of course, we don't return any task that has already been\n",
" completed.\"\"\"\n",
" # YOUR CODE HERE\n",
" return ({t for t in self.tasks \n",
" if self.predecessors[t].issubset(self.completed_tasks)}\n",
" - self.completed_tasks)\n",
"\n",
"def scheduler_mark_completed(self, t):\n",
" \"\"\"Marks the task t as completed, and returns the additional\n",
" set of tasks that can be done (and that could not be\n",
" previously done) once t is completed.\"\"\"\n",
" # YOUR CODE HERE\n",
" for p in self.predecessors[t]:\n",
" if p in self.uncompleted:\n",
" raise IllegalCompletion(t)\n",
" \n",
" self.completed_tasks.add(t)\n",
" return {u for u in self.successors[t] \n",
" if self.predecessors[u].issubset(self.completed_tasks)}\n",
"\n",
"DependencyScheduler.available_tasks = property(scheduler_available_tasks)\n",
"DependencyScheduler.mark_completed = scheduler_mark_completed\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"deletable": false,
"id": "RnTGjkVhkacM",
"nbgrader": {
"checksum": "9cbfd3fceef4fc700ee6fd8765c94e66",
"grade": false,
"grade_id": "cell-ab6c2cd521650cd4",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"# Here is a place where you can test your code. \n",
"\n",
"# YOUR CODE HERE"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "fbDAHS7IkacM",
"nbgrader": {
"checksum": "9abcbb7a57e188e7f19741fdef160c7f",
"grade": false,
"grade_id": "cell-3e19369eeb9a643d",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Let us check if this works."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"deletable": false,
"editable": false,
"id": "5OGt-_BgkacM",
"nbgrader": {
"checksum": "34c28ee0d4c755735562b962526341d2",
"grade": false,
"grade_id": "cell-92b59a47fe7f0336",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "ac6123c0-6313-4ebd-c8e5-4a85b59bc0a6"
},
"outputs": [],
"source": [
"# Let us ensure that nose is installed.\n",
"try:\n",
" from nose.tools import assert_equal, assert_true\n",
" from nose.tools import assert_false, assert_almost_equal\n",
"except:\n",
" !pip install nose\n",
" from nose.tools import assert_equal, assert_true\n",
" from nose.tools import assert_false, assert_almost_equal\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 353
},
"deletable": false,
"editable": false,
"id": "6vuF6du9kacM",
"nbgrader": {
"checksum": "af201e981d4b861bf954b7e2384b3e9a",
"grade": false,
"grade_id": "cell-d9857dde3e277260",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "1f85633c-afa2-40fb-e026-17c3729ff9d6"
},
"outputs": [
{
"data": {
"image/png": "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obNnz1Zbqnl5eWrfvv11pVr1Y/fu3RUREeH9m1dWSp06SRcvev30SUlzJeVKCpH0c0k3bZmKjHTNaB40yFd/ZOCWWOmaxL2ZitJFsHI6nbpw4UK1pXrixAnFxMQoISHBU6ZDhgzRAw88oISEBPXo0UMtWrSo3w8PDZXmzZNefFEqLb3p0z0kfXir73HbbRQuDMdK1yTz589Xly5dtGDBArOjANW6dOnSTYXq/vXx48cVERHhdZUaHx+v+Ph4RUVF+S/cuXNSnz5ebzHfUmSk9P770qRJvs8F1ICVrkkSExP18ccfmx0DTVxBQYHXVar7o8PhuK5Me/furZSUFE+ptmrVyrzwHTu6zuumpbkmTNVSsaTSGTPUlsKFCShdkwwcOFBvvfWW2THQyBUXF+vEiRPVlmpJSYmnQN3l+tOf/tTz723atFFIIL8GLynJVbxWq2s6VTUTqiS5XufXsqUOpKTo/sxM5eblKS4uzrisgLi9bJrLly8rLi5OV65cYQcz6u3atWs6efLkTbd+3R8vX76sHj16XPdcterHDh06BHap1tbJk9LLL0srVrgmVVU9f+t+bjxunLRwoTRihJYtW6bly5crJydHnTp1MiczmiRK10Tdu3fXjh071LNnT7OjIEBVVFQoLy+v2lvA58+fV7du3ap9rtqlS5em9T91paXS3/8uffaZdP686xxur17StGlS587XXbp48WKtW7dO2dnZatOmjUmB0dRQuiZKTU3VL3/5S02YMMHsKDCJ+6xqdaV65swZderUqdpS7datm8LCeEpUH06nU08//bR27NihrKwsxcTEmB0JTQCla6Inn3xS3bp109NPP212FPiJ0+nUd999V22p5uXlqV27dtWWalxcXPVnVdFgTqdTs2fP1tGjR5WZmVn/I0xALVG6Jvrzn/+sXbt26e233zY7CurJ6XTq4sWLXo/UHDt2zHNWtboBED179uQvepNVVlZq+vTpKiws1Lp16xQeHm52JDRilK4JnE6nsrOz9cEHH2jVqlXq2LGjbr/9dv3tb38zOxq8uHz5crW7f48fP66wsLBqNyr17NlT0dHRZv8RcAvl5eW6//77FRUVpffee0+hoaFmR0IjRemaoLS0VO3bt1dpaakqKysVEhKi2bNna/ny5WZHa5IKCwurLdVjx46psrKy2lI1/awqfKa0tFQTJ05Ur169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\n",
"text/plain": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from nose.tools import assert_true, assert_false, assert_equal\n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', ['b', 'c'])\n",
"s.add_task('b', ['c', 'e'])\n",
"s._check()\n",
"s.show()\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "giXGuSh2kacM",
"nbgrader": {
"checksum": "b6d6104b2ca8bd21eb6fc98c96471d59",
"grade": false,
"grade_id": "cell-236e1fcb5f373d17",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"We note that in the above drawing, the edges denote temporal succession, that is, an edge from $c$ to $a$ means that $c$ must happen before $a$. \n",
"Let us execute the schedule manually."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "aMadMw9OkacM",
"nbgrader": {
"checksum": "ff1dbebea8f5468f341e70ee112d2541",
"grade": false,
"grade_id": "cell-a448f628a33f1655",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Here are some tests for `available_tasks` and `mark_completed`. "
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"deletable": false,
"editable": false,
"id": "DmB_OitvkacM",
"nbgrader": {
"checksum": "87f4aef1da9401c8b1e14ea98c65e07b",
"grade": true,
"grade_id": "cell-97a947c37868d065",
"locked": true,
"points": 5,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"### Simple tests. 5 points. \n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', [])\n",
"assert_equal(s.available_tasks, {'a'})\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"deletable": false,
"editable": false,
"id": "63Y9m0-ekacM",
"nbgrader": {
"checksum": "1c260d93e494b94353ee43c9986a912b",
"grade": true,
"grade_id": "cell-e63cebb9c5161e17",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"### Slightly more complicated. 10 points. \n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', ['b', 'c'])\n",
"s.add_task('b', ['c', 'e'])\n",
"assert_equal(s.available_tasks, {'e', 'c'})\n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', ['b'])\n",
"s.add_task('b', ['a'])\n",
"assert_equal(s.available_tasks, set())\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"deletable": false,
"editable": false,
"id": "_emz5xo7kacM",
"nbgrader": {
"checksum": "3d976d40ecb3e0aea04ab706c381b6a0",
"grade": true,
"grade_id": "cell-fdc5d30f849014af",
"locked": true,
"points": 5,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"### Now, let's test `mark_completed`. Simple tests first. 5 points. \n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', [])\n",
"assert_equal(s.available_tasks, {'a'})\n",
"r = s.mark_completed('a')\n",
"assert_equal(r, set())\n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', ['b'])\n",
"assert_equal(s.available_tasks, {'b'})\n",
"r = s.mark_completed('b')\n",
"assert_equal(r, {'a'})\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"deletable": false,
"editable": false,
"id": "iUfccczckacM",
"nbgrader": {
"checksum": "bc1219453bf2f8e01785141255fd7f1d",
"grade": true,
"grade_id": "cell-71c82e71e3d5c769",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"### Slightly more complicated. 10 points. \n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', ['b', 'c'])\n",
"assert_equal(s.available_tasks, {'b', 'c'})\n",
"r = s.mark_completed('b')\n",
"assert_equal(r, set())\n",
"assert_equal(s.available_tasks, {'c'})\n",
"r = s.mark_completed('c')\n",
"assert_equal(r, {'a'})\n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', ['b', 'c'])\n",
"s.add_task('b', ['c', 'e'])\n",
"s.add_task('c', [])\n",
"assert_equal(s.available_tasks, {'c', 'e'})\n",
"r = s.mark_completed('e')\n",
"assert_equal(r, set())\n",
"r = s.mark_completed('c')\n",
"assert_equal(r, {'b'})\n",
"r = s.mark_completed('b')\n",
"assert_equal(r, {'a'})\n",
"r = s.mark_completed('a')\n",
"assert_equal(r, set())\n",
"assert_equal(s.available_tasks, set())\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "4Si-0n65kacM",
"nbgrader": {
"checksum": "c0e617b3ab44b02e965cd7181c719847",
"grade": false,
"grade_id": "cell-1fdbf05f74676a81",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Executing the tasks\n",
"\n",
"Here is an execution engine for our tasks with dependencies."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"deletable": false,
"editable": false,
"id": "03fwlwB-kacM",
"nbgrader": {
"checksum": "121aee66c911faf16ef686e9fcffa102",
"grade": false,
"grade_id": "cell-c285056bbc55c15",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"import random\n",
"\n",
"def execute_schedule(s, show=False):\n",
" s.reset()\n",
" in_process = s.available_tasks\n",
" print(\"Starting by doing:\", in_process)\n",
" while len(in_process) > 0:\n",
" # Picks one random task to be the first to be completed.\n",
" t = random.choice(list(in_process))\n",
" print(\"Completed:\", t)\n",
" in_process = in_process - {t} | s.mark_completed(t)\n",
" print(\"Now doing:\", in_process)\n",
" if show:\n",
" s.show()\n",
" # Have we done all?\n",
" if not s.done:\n",
" print(\"Error, there are tasks that could not be completed:\", s.uncompleted)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "JTZ1_0ffkacM",
"nbgrader": {
"checksum": "0b89ce00f896cfb7dd641a00c2c61d86",
"grade": false,
"grade_id": "cell-5c41d5760a16bd50",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Let's try it on our old schedule:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 353
},
"deletable": false,
"editable": false,
"id": "fwFI8zF5kacM",
"nbgrader": {
"checksum": "438c73779c99e682ebebf7c068db85c2",
"grade": false,
"grade_id": "cell-47bed31601afff35",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "5b02df8d-780a-4e28-eb73-28333d7383d1"
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAd0AAAFACAYAAAD5xabzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzt3XtYVXWi//E3CAgIeUFNnUjxUoNddMobITV5q45WYqOhY2qlHs+oHU072fwG0BTPpJbl2PN4qXRImXHMI1QDZwrNUAEbnTHNQsWDpIiaKEgoCHuv3x87Hc2NicJa+/J5PY+Pl7329kM1fOZ7Wd/lYxiGgYiIiDQ4X6sDiIiIeAuVroiIiElUuiIiIiZR6YqIiJhEpSsiImISla6IiIhJVLoiIiImUemKiIiYRKUrIiJiEpWuiIiISVS6IiIiJlHpioiImESlKyIiYhKVroiIiElUuiIiIiZR6YqIiJhEpSsiImISla6IiIhJVLoiIiImUemKiIiYRKUrIiJiEpWuiIiISfysDiAiIlIn338POTlQUgK+vhAWBtHREBhodbKfpNIVERH38M038OabsGYN+PmBYTj+3McH7HZ4/nl44QXo2NHanNfgYxgXU4uIiLggmw1+8xt4/32oroaaGufX+ftDo0bwn/8J//3fjjJ2MSpdERFxXTYbDB0KmzfDuXPX957gYBgxAt57z+WKVxupRETEdc2YUbfCBce1f/mLY7TrYjTSFRER13TyJLRvD5WVN/b+4GDHZzRpUr+5boJGuiIi4ppWrLi59/v4QEpK/WSpJxrpioiI67HZoE0bOHXK6cu/B1YCJ4FwIAmIdXZhp06Qn99QKetMI10REXE9RUXXXMftBGwFyoBEYDRQ7OzCw4frth7cwFS6IiLiekpLHffi1mI40A5HiT0NdAG+cHZhQIDjs1yESldERFxPQMC/Dr9wIhnoDjT74cdXgNOJaLvd8VkuQidSiYiI62ndGqqqnL5UCEwANgFRQCMcBey0ou12aNasgULWnUa6IiLielq0gJ49nb5UAfgArX74/SocI92r+PrCE09cc5rabK6TREREvNbJkycpKCjAZrNht9s5f/487WNjuWPPHigvv+LarsAMHKNcX2AMEO3sQ4OCYObMho5eJ7plSERELPf888/z/vvvExwczIULFzh/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5YvatIG773b6Ui/gFuA14DyOUe5XOKadr+DrC089dfM7oEVExCOpdC/38stOR6mNgI+A3ThGvC2B8UDZjy8MCoIZMxo4pIiIuCvdMnS56mrHNHMttw1dk48P/Pznjt3LIiIiTmikezl/f8fhFkFBdX9vkyawfn39ZxIREY+h0v2xfv0cj/ILDr6+6318HFPS//u/joMxREREaqHSdeapp+DTT+Heex3l26jR1df4+zvOWH7gAfjiC4iONj+niIi4Fa3p/pQvv4Q33oD0dPj+e8cO5VtugaefhqlToVMnqxOKiIibUOmKiIiYRNPLIiIiJlHpioiImESlKyIiYhKVroiIiElUuiIiIiZR6YqIiJhEpSsiImISla6IiIhJVLoiIiImUemKiIiYRKUrIiJiEpWuiIiISVS6IiIiJlHpioiImESlKyIiYhKVroiIiElUuiIiIiZR6YqIiJhEpSsiImISla6IiIhJVLoiIiImUemKiIiYRKUrIiJiEpWuiIiISVS6IiIiJlHpioiImESlKyIiYhKVroiIiElUuiIiIiZR6YqIiJhEpSsiImISla6IiIhJVLoiIiImUemKiIiYRKUrIiJiEpWuiIiISVS6IiIiJlHpioiImESlKyIiYpL/D8HepvQA0zT0AAAAAElFTkSuQmCC\n",
"text/plain": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"s = DependencyScheduler()\n",
"s.add_task('a', ['b', 'c'])\n",
"s.add_task('b', ['c', 'e'])\n",
"s._check()\n",
"s.show()\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"deletable": false,
"editable": false,
"id": "8GLvAT5MkacM",
"nbgrader": {
"checksum": "49402dc6b29c411541e4c9eb0d59eaa7",
"grade": false,
"grade_id": "cell-cb9ee8db2218be70",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "a26e52ce-2f7b-4886-d5da-f97f008d6ad0"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting by doing: {'e', 'c'}\n",
"Completed: e\n",
"Now doing: {'c'}\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Completed: c\n",
"Now doing: {'b'}\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Completed: b\n",
"Now doing: {'a'}\n"
]
},
{
"data": {
"image/png": "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yxdffEHbtm3NjiMiIibQOV03cDqdjBw5ksmTJ6twRUT8mErXDaZMmUJeXh5vv/222VFERMREml52sW+++YZnnnmGnTt30rBhQ7PjiIiIiTTSdaGcnBwiIiL429/+psIVERGNdF3prbfe4vjx4yxdulSbYIiIiC4ZcpUNGzaQmJjI3r17VbgiIgJoetklrl69yqhRo/j000+165SIiNyi6WUXePnll6levTrR0dFmRxEREQ+i6eVSdvNGBl9//bXZUURExMNopFuKTp48Sdu2bVm7di1t2rQxO46IiHgYndMtJTd3nfrNb36jwhURkUKpdEvJ3//+d+x2O//5n/9pdhQREfFQml4ugut519lxegdXblwhMCCQ6qHV6VC3AyFBIQCkpKTw7LPPkpycTIMGDcwNKyIiHksLqe7h8KXDTNk+hYRvEigT+NO/qgACeOOXb/Bay9cYNmwY//d//6fCFRGRe9JItxB2p53xq8Yz/5v55DvzsTvthR5XNqgsdrudZheasS9mH4GBmq0XEZG7U0v8jMPpoP+i/izYt4Ab9ht3LVyAXEcujgAHx+scJ3JNpBtTioiIN1Lp/szkNZPZeHwj2fnZRX5Ntj0b214b1h1WFyYTERFvp+nl25zNPEtDa0NyHbklen2FkApc/N1FypUpV8rJRETEF2ike5vpX09/4JsTLD24tJTSiIiIr9FI9wd2p52af6vJ1ZyrhR9wDfgCOAGEAB1++PMzT9R4gv1v7ndZThER8V66ZOgHx9OPk+fIK/ydTmAh0AwYREEBzwWqA41/eujBiwexO+13XGIkIiKi6eUfpOek370ozwDXge4U/JpSFWgLFDKgDQkKIT0n3UUpRUTEm2k49oPgwGAM7jLTng5kAh/f9pwBPHLnoU7DeWunKhERkdupdH/wUIWHyLXfZdVyJaAKUMRLccNDwksrloiI+BBNL/+gVoVatKjZovB31gHKAluAfArO8Z4HTv/0sMCAQAY1H/TAK6BFRMQ3qXRv83bntwsfpQYCLwPngCnA/w+sAHJ+elj5MuV5q9Nbro4pIiJeSpcM3Sbfkc9D//vQ3S8buocAAnis+mMcnHDQBclERMQXaKR7m+CgYJYOXUr5MuWL/dqwkDCWDFniglQiIuIrVLo/07NhTxIGJhBaJrRIxwcQQHhIOGuGreGJmk+4OJ2IiHgzTS/fxVenvmL8qvGkXkkl156Lw3D85P3BgcEEBQbR9uG2zOo3i8eqP2ZSUhER8RYq3ftIOZfC37f/ndXfrSYrL4vAgEAqhlTkpRYvMan9JB6t+qjZEUVExEuodEVERNxE53RFRETcRKUrIiLiJipdERERN1HpioiIuIlKV0RExE1UuiIiIm6i0hUREXETla6IiIibqHRFRETcRKUrIiLiJipdERERN1HpioiIuIlKV0RExE1UuiIiIm6i0hUREXETla6IiIibqHRFRETcRKUrIiLiJipdERERN1HpioiIuIlKV0RExE1UuiIiIm6i0hUREXETla6IiIibqHRFRETcRKUrIiLiJipdERERN1HpioiIuIlKV0RExE1UuiIiIm6i0hUREXETla6IiIibqHRFRETcRKUrIiLiJipdERERN1HpioiIuIlKV0RExE1UuiIiIm6i0hUREXGT/wc7nWO2LCMYOwAAAABJRU5ErkJggg==\n",
"text/plain": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Completed: a\n",
"Now doing: set()\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"execute_schedule(s, show=True)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "lAyaUHLmkacN",
"nbgrader": {
"checksum": "a3c215f20358f8f865a4b0090cf010ab",
"grade": false,
"grade_id": "cell-9c9e6ce16186ae03",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"What happens if there is a loop? "
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"deletable": false,
"editable": false,
"id": "BjwBKj05kacN",
"nbgrader": {
"checksum": "1a72092331547b48af83b369cef694b7",
"grade": false,
"grade_id": "cell-2dc9924f19540ab3",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "97c8c6d9-520d-488d-d20b-817c552dbb08"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting by doing: set()\n",
"Error, there are tasks that could not be completed: {'b', 'c', 'a'}\n"
]
}
],
"source": [
"s = DependencyScheduler()\n",
"s.add_task('a', ['b'])\n",
"s.add_task('b', ['a'])\n",
"s.add_task('c', ['a'])\n",
"execute_schedule(s)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "Y7SYMbISkacN",
"nbgrader": {
"checksum": "4b32a15d1debae4980463aa52d8c7d01",
"grade": false,
"grade_id": "cell-1d09ba345dc1de32",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Ok, this is reasonable! Let us now encode our Carbonara pasta recipe. "
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 353
},
"deletable": false,
"editable": false,
"id": "B4HKEQVukacN",
"nbgrader": {
"checksum": "a08bcaa837cddd038962a11f91fb8343",
"grade": false,
"grade_id": "cell-bc314c96d21a34c7",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "10b6b2d6-fff5-4f5f-fbe1-fcb6b8afeaa9"
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"
"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"carbonara = DependencyScheduler()\n",
"\n",
"# First, the part about cooking the pancetta.\n",
"carbonara.add_task('dice onions', [])\n",
"carbonara.add_task('dice pancetta', [])\n",
"carbonara.add_task('put oil and butter in pan', [])\n",
"carbonara.add_task('put pancetta in pan', ['dice pancetta'])\n",
"carbonara.add_task('put onions in pan', ['dice onions'])\n",
"carbonara.add_task('cook pancetta', ['put oil and butter in pan',\n",
" 'put pancetta in pan',\n",
" 'put onions in pan'])\n",
"\n",
"# Second, the part about beating the eggs.\n",
"carbonara.add_task('put eggs in bowl', [])\n",
"carbonara.add_task('beat eggs', ['put eggs in bowl'])\n",
"\n",
"# Third, cooking the pasta.\n",
"carbonara.add_task('fill pot with water', [])\n",
"carbonara.add_task('bring pot of water to a boil', ['fill pot with water'])\n",
"carbonara.add_task('add salt to water', ['bring pot of water to a boil'])\n",
"carbonara.add_task('put pasta in water', ['bring pot of water to a boil',\n",
" 'add salt to water'])\n",
"carbonara.add_task('colander pasta', ['put pasta in water'])\n",
"\n",
"# And finally, we can put everything together.\n",
"carbonara.add_task('serve', ['beat eggs', 'cook pancetta', 'colander pasta'])\n",
"\n",
"# Let's look at our schedule!\n",
"carbonara.show()\n"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"deletable": false,
"editable": false,
"id": "Ncr1M1LTkacN",
"nbgrader": {
"checksum": "b71b1bfa89a45be334e3eb7bb1ffe580",
"grade": false,
"grade_id": "cell-dde3ea491011fe74",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "c2c72843-3ca6-4c6a-98ad-4daf6c5081a1"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Starting by doing: {'dice onions', 'put eggs in bowl', 'dice pancetta', 'put oil and butter in pan', 'fill pot with water'}\n",
"Completed: put eggs in bowl\n",
"Now doing: {'dice onions', 'dice pancetta', 'put oil and butter in pan', 'fill pot with water', 'beat eggs'}\n",
"Completed: put oil and butter in pan\n",
"Now doing: {'dice onions', 'fill pot with water', 'beat eggs', 'dice pancetta'}\n",
"Completed: dice pancetta\n",
"Now doing: {'dice onions', 'fill pot with water', 'beat eggs', 'put pancetta in pan'}\n",
"Completed: beat eggs\n",
"Now doing: {'dice onions', 'fill pot with water', 'put pancetta in pan'}\n",
"Completed: put pancetta in pan\n",
"Now doing: {'dice onions', 'fill pot with water'}\n",
"Completed: dice onions\n",
"Now doing: {'put onions in pan', 'fill pot with water'}\n",
"Completed: fill pot with water\n",
"Now doing: {'put onions in pan', 'bring pot of water to a boil'}\n",
"Completed: bring pot of water to a boil\n",
"Now doing: {'put onions in pan', 'add salt to water'}\n",
"Completed: put onions in pan\n",
"Now doing: {'add salt to water', 'cook pancetta'}\n",
"Completed: add salt to water\n",
"Now doing: {'cook pancetta', 'put pasta in water'}\n",
"Completed: put pasta in water\n",
"Now doing: {'colander pasta', 'cook pancetta'}\n",
"Completed: cook pancetta\n",
"Now doing: {'colander pasta'}\n",
"Completed: colander pasta\n",
"Now doing: {'serve'}\n",
"Completed: serve\n",
"Now doing: set()\n"
]
}
],
"source": [
"# And let's finally prepare carbonara!\n",
"execute_schedule(carbonara)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "lo45Mr8YkacN",
"nbgrader": {
"checksum": "7bade197fa4e480a09146b22ef8afb25",
"grade": false,
"grade_id": "cell-daa0327fa3f623ef",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"This is not necessarily the best order of actions to prepare pasta carbonara, but it definitely works as a schedule."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "GtCxMENWkacN",
"nbgrader": {
"checksum": "32d632a86ab53799e90ac07e4bdbec37",
"grade": false,
"grade_id": "cell-62458aa5f6bd6db0",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Building a Better Execution Engine"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "z0ujdvV-kacN",
"nbgrader": {
"checksum": "1c62725f1ed690ccb49e8526dec0f1c8",
"grade": false,
"grade_id": "cell-68a98344892ae6e4",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Let us build a better execution engine for our schedules. Right now, we have a function:"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"deletable": false,
"editable": false,
"id": "y9mNp7cdkacN",
"nbgrader": {
"checksum": "acea01d4083f8b34a8913c0d277fa2af",
"grade": false,
"grade_id": "cell-ee813cfbdf456ac5",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"def execute_schedule(s, show=False):\n",
" s.reset()\n",
" in_process = s.available_tasks\n",
" print(\"Starting by doing:\", in_process)\n",
" while len(in_process) > 0:\n",
" # Picks one random task to be the first to be completed.\n",
" t = random.choice(list(in_process))\n",
" print(\"Completed:\", t)\n",
" in_process = in_process - {t} | s.mark_completed(t)\n",
" print(\"Now doing:\", in_process)\n",
" if show:\n",
" s.show()\n",
" # Have we done all?\n",
" if not s.done:\n",
" print(\"Error, there are tasks that could not be completed:\", s.uncompleted)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "vzMENNIqkacN",
"nbgrader": {
"checksum": "6c135f48b406d3d04b197904ef911090",
"grade": false,
"grade_id": "cell-3c03e5954721ecbc",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"We want to wrap these methods into a class, RunSchedule. This will allow us more flexibility in executing a schedule, as we will be able to specify parameters that guide the execution policy, interrupt and resume the execution, and so on. \n",
"An object of class RunSchedule is initialized with a DependencyScheduler. It then has the following methods: \n",
"\n",
"* **reset:** mark all tasks as not completed. \n",
"* **step:** perform one step in the schedule, completing a single task.\n",
"* **run:** performs all steps in the schedule, until completion. \n",
"* **done:** indicates that all tasks have been done.\n",
"\n",
"What should these methods return? _step_ will return the task executed, while _run_ will return the whole list of tasks, in the order in which they were done. "
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"deletable": false,
"editable": false,
"id": "BQWFGtRKkacN",
"nbgrader": {
"checksum": "baeffbbc770c2247d1b401f184c7aaf4",
"grade": false,
"grade_id": "cell-419c455e1fa49a35",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"class RunSchedule(object):\n",
"\n",
" def __init__(self, scheduler):\n",
" self.scheduler = scheduler\n",
" self.in_process = None # Indicating, we don't know yet.\n",
"\n",
" def reset(self):\n",
" self.scheduler.reset()\n",
" self.in_process = None\n",
"\n",
" def step(self):\n",
" \"\"\"Performs a step, returning the task, if any, or None,\n",
" if there is no step that can be done.\"\"\"\n",
" # If we don't know what steps are in process, we get them.\n",
" if self.in_process is None:\n",
" self.in_process = self.scheduler.available_tasks\n",
" if len(self.in_process) == 0:\n",
" return None\n",
" t = random.choice(list(self.in_process))\n",
" self.in_process = self.in_process - {t} | self.scheduler.mark_completed(t)\n",
" return t\n",
"\n",
" @property\n",
" def done(self):\n",
" return self.scheduler.done\n",
"\n",
" def run(self):\n",
" \"\"\"Runs the scheduler from the current configuration to completion.\n",
" You must call reset() first, if you want to run the whole schedule.\"\"\"\n",
" tasks = []\n",
" while not self.done:\n",
" t = self.step()\n",
" if t is not None:\n",
" tasks.append(t)\n",
" return tasks\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "wRSN_6JfkacN",
"nbgrader": {
"checksum": "d9a54aa7a503239bf81f2463cb74319f",
"grade": false,
"grade_id": "cell-50337ca62797b2cc",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"We can run our pasta carbonara with this RunSchedule class:"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"deletable": false,
"editable": false,
"id": "_9bOnhqpkacN",
"nbgrader": {
"checksum": "7cd3e1103d79f0bd0bac7e4e02f77301",
"grade": false,
"grade_id": "cell-a0b0e7d1e74b4d33",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "3ddaa8df-9d58-44ba-aaed-b8dff5cd5358"
},
"outputs": [
{
"data": {
"text/plain": [
"['dice onions',\n",
" 'put onions in pan',\n",
" 'put eggs in bowl',\n",
" 'beat eggs',\n",
" 'dice pancetta',\n",
" 'fill pot with water',\n",
" 'put oil and butter in pan',\n",
" 'put pancetta in pan',\n",
" 'cook pancetta',\n",
" 'bring pot of water to a boil',\n",
" 'add salt to water',\n",
" 'put pasta in water',\n",
" 'colander pasta',\n",
" 'serve']"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"runner = RunSchedule(carbonara)\n",
"runner.reset()\n",
"runner.run()\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "gB0_0GlnkacN",
"nbgrader": {
"checksum": "1bc2fd9f9c1b7e56a64bb85bc56df26e",
"grade": false,
"grade_id": "cell-d160f03528fef7eb",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Let us pause for a moment and ask: did we really need to create a new class? Could we not have done the above in the scheduler class? \n",
"\n",
"This is debatable. The idea in keeping the two classes separate is clarity of goals: \n",
"\n",
"* The scheduler is concerned with what _can_ be done next. \n",
"* The runner is concerned with any practical constraint to the execution, and with the choice of _what_, among the possible, is actually done. \n",
"\n",
"We will have occasion below to rely on this division of concerns."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "VmUnjXuCkacN",
"nbgrader": {
"checksum": "4bdadeaf1a6dfab0650df33f9282634c",
"grade": false,
"grade_id": "cell-ab56ae966c12c39d",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"### Code changes and rotten eggs"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "dYaHPtGVkacN",
"nbgrader": {
"checksum": "59bf5e852f155980256b6ef310bee302",
"grade": false,
"grade_id": "cell-10062b48fd612300",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"#### Code changes\n",
"\n",
"Imagine that you need to compile three programs, $a$, $c$, and then link together the results into $f.out$. Once this is done, you compile $d$ and $e$, and link into $g.out$. As the last step, you link the two libraries $g.out$ and $f.out$ together, and produce $h$. You do it once. Great. But now you realize that you need to change $b$. Do you have to start from scratch? \n",
"\n",
"You may think, who cares, it's the CPU doing the work, not me. Fair enough, but there are some large systems that take minutes, dozen of minutes, to compile. If you are compiling the linux kernel on a low power CPU, it might take hours. Surely you don't want to redo everything from scratch! \n",
"\n",
"So imagine you have the tasks in an intermediate state, with some being completed (possibly all of them), and some not. You can now mark one of the tasks as incomplete, to signal you need to do it again. What is the set of tasks that as a consequence should also be marked incomplete? \n",
"If you have two tasks $x$ and $y$, with $y$ being a successor to $x$, if $x$ is marked as \"undone\" as it needs to be redone, then also $y$ will need to be redone, as it might use the results of $x$.\n",
"\n",
"To implement this, we will perform two modifications. First, we will endow our scheduler with a _redo_ method, which marks a task and all its successors to be redone -- that is, it _unmarks_ them as done. We let you implement this; you have already seen how to compute reachability in the graph chapter. \n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "4VKN56WzkacN",
"nbgrader": {
"checksum": "f4f62ac8c425709abda425a8ad8b639b",
"grade": false,
"grade_id": "cell-9f80ab8aa77e6100",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Question 2: redo for code"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"deletable": false,
"id": "eMfe4d1nkacN",
"nbgrader": {
"checksum": "dda6cc12b8a5445c115239cfadc9289d",
"grade": false,
"grade_id": "cell-80715adbd3dac83e",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"### Implementation of `redo`\n",
"\n",
"def dependency_scheduler_redo(self, t):\n",
" \"\"\"Mark the task t, and all its successors, as undone.\n",
" Returns the set of successor tasks of t, with t included.\"\"\"\n",
" # YOUR CODE HERE\n",
" tasks = set()\n",
" tasks.add(t)\n",
" if t in self.completed_tasks:\n",
" self.completed_tasks.remove(t)\n",
" for s in self.successors[t]:\n",
" tasks.add(s)\n",
" for x in dependency_scheduler_redo(self, s):\n",
" tasks.add(x)\n",
" return tasks\n",
"\n",
"DependencyScheduler.redo = dependency_scheduler_redo\n"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"deletable": false,
"id": "6CaaXn0HkacN",
"nbgrader": {
"checksum": "5585de9f76f2078b994256defd546daa",
"grade": false,
"grade_id": "cell-ffbf3c1f4fa3bdd2",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"# Here is a place where you can test your code. \n",
"\n",
"# YOUR CODE HERE"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "AE0YhFAXkacN",
"nbgrader": {
"checksum": "65022df11bcbd0b2cfaf5c4408754571",
"grade": false,
"grade_id": "cell-5b62e886f0ae12ea",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Let us test the implementation."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"deletable": false,
"editable": false,
"id": "AP8H7FmhkacN",
"nbgrader": {
"checksum": "53c69adb193594c1059c599021d9205b",
"grade": true,
"grade_id": "cell-8357360c4a918edb",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"### Tests for `redo` for code. 10 points. \n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', [])\n",
"s.add_task('b', ['a'])\n",
"s.add_task('c', ['a'])\n",
"s.add_task('d', ['b', 'c'])\n",
"s.add_task('e', ['a', 'd'])\n",
"\n",
"s.mark_completed('a')\n",
"s.mark_completed('b')\n",
"s.mark_completed('c')\n",
"assert_equal(s.available_tasks, {'d'})\n",
"s.redo('b')\n",
"assert_equal(s.available_tasks, {'b'})\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "dvAbNHUukacN",
"nbgrader": {
"checksum": "f8f408a0c22b5baae9ebc0394d3e18f7",
"grade": false,
"grade_id": "cell-dc8e08c747c3f414",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Next, we implement a runner that has an additional operation _redo(t)_ for a task t. "
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"deletable": false,
"editable": false,
"id": "v8Lqw1mnkacN",
"nbgrader": {
"checksum": "4ed044492ef70632de7970d20b137fb5",
"grade": false,
"grade_id": "cell-ef3550fd3be8ba89",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"outputs": [],
"source": [
"def run_schedule_redo(self, t):\n",
" \"\"\"Marks t as to be redone.\"\"\"\n",
" # We drop everything that was in progress.\n",
" # This also forces us to ask the scheduler for what to redo.\n",
" self.in_process = None\n",
" return self.scheduler.redo(t)\n",
"\n",
"RunSchedule.redo = run_schedule_redo\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "0eQ0DQe9kacN",
"nbgrader": {
"checksum": "3f4ec294c647cca80d50e650f19723d8",
"grade": false,
"grade_id": "cell-649d49e90f721cf7",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"We can now play with it. "
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"deletable": false,
"editable": false,
"id": "FyhvQByTkacN",
"nbgrader": {
"checksum": "0b03de64cc41e4a31cc5b46ccefd49e4",
"grade": false,
"grade_id": "cell-e75c320e4a31c2d1",
"locked": true,
"schema_version": 1,
"solution": false
},
"outputId": "17d36e43-85a8-4412-eaa5-b97ba6160e09"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"dice onions\n",
"fill pot with water\n",
"bring pot of water to a boil\n",
"dice pancetta\n",
"add salt to water\n",
"put eggs in bowl\n",
"put oil and butter in pan\n",
"beat eggs\n",
"put onions in pan\n",
"put pasta in water\n",
"---> readd salt\n",
"marking undone: {'colander pasta', 'add salt to water', 'put pasta in water', 'serve'}\n",
"completed: {'put onions in pan', 'dice onions', 'put eggs in bowl', 'bring pot of water to a boil', 'put oil and butter in pan', 'dice pancetta', 'fill pot with water', 'beat eggs'}\n",
"put pancetta in pan\n",
"cook pancetta\n",
"add salt to water\n",
"put pasta in water\n",
"colander pasta\n",
"serve\n",
"None\n",
"None\n",
"None\n",
"None\n",
"--->redo dice pancetta\n",
"marking undone: {'put pancetta in pan', 'serve', 'cook pancetta', 'dice pancetta'}\n",
"completed: {'put onions in pan', 'dice onions', 'put eggs in bowl', 'bring pot of water to a boil', 'put oil and butter in pan', 'colander pasta', 'fill pot with water', 'add salt to water', 'beat eggs', 'put pasta in water'}\n",
"dice pancetta\n",
"put pancetta in pan\n",
"cook pancetta\n",
"serve\n"
]
}
],
"source": [
"runner = RunSchedule(carbonara)\n",
"runner.reset()\n",
"for _ in range(10):\n",
" print(runner.step())\n",
"print(\"---> readd salt\")\n",
"print(\"marking undone:\", runner.redo(\"add salt to water\"))\n",
"print(\"completed:\", runner.scheduler.completed_tasks)\n",
"for _ in range(10):\n",
" print(runner.step())\n",
"print(\"--->redo dice pancetta\")\n",
"print(\"marking undone:\", runner.redo(\"dice pancetta\"))\n",
"print(\"completed:\", runner.scheduler.completed_tasks)\n",
"for t in runner.run():\n",
" print(t)\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "GvHfXhTrkacN",
"nbgrader": {
"checksum": "fefa652c64562e34bb0febc03b8e5c8c",
"grade": false,
"grade_id": "cell-d879d3343e6f13f3",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"You have learned to sequence the order in which to do tasks so as to respect their dependencies. In the next chapter, we will learn how to also take into account the time it takes for us to do the tasks. In the meantime, bon appetit, or rather, guten appetit, or rather, buon appetito!"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "YFAQC0CFkacN",
"nbgrader": {
"checksum": "762427c225bf731e0bb90204533754a7",
"grade": false,
"grade_id": "cell-d8ce6c37cba9be0",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"#### Redoing in cooking\n",
"\n",
"The act of redoing a cooking step is somewhat different than the act of redoing something in code. Suppose you cook pasta, unite with it the fried bacon and onions, and then -- terrible mishap -- you unite with it the beaten egg yolks in which one of the eggs is rotten. \n",
"\n",
"In code, when one file changes, you only need to redo the things that _depend_ on that file. In cooking, it is different: even if nothing changed in the bacon, onions, and cooked pasta, once you add to it rotten eggs you have to redo the pasta, bacon, onions, etc, as well, as they have now been contaminated. The root of the problem is that in a makefile, when you combine two files to compute a result, you do not destroy the original files, whereas in cooking, once you combine foods, you don't have the original foods any longer. Cooking is like a makefile in which, once you combine files, you immediately delete them. \n",
"\n",
"So let us come up with a precise definition of what needs to be redone in cooking, when one of the steps goes bad (the eggs are rotten, you burn the food on the stove, and so on). \n",
"\n",
"Initially, we label _redo_ the task that needs redoing. We then propagate the label according to these two rules: \n",
"\n",
"* If a task $v$ is labeled _redo_, if $u$ is a successor of $v$ and $u$ is completed, then $u$ is also labeled _redo_. \n",
"* If a task $v$ is labeled _redo_, and if $u$ is a predecessor of $v$, then $u$ is also labeled _redo_ (note that in this case, we are guaranteed that $u$ is completed). \n",
"\n",
"The first rule corresponds to a _forward_ pass in the dependency garph; the second rule corresponds to a _backward_ pass in the dependency relation. \n",
"Once the _redo_ label is propagated, all tasks that are marked _redo_ are changed from completed, to uncompleted. \n",
"\n",
"We ask you to implement this in code."
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "t3763UgKkacN",
"nbgrader": {
"checksum": "bc530f1067c9f06847e1fa513a92990c",
"grade": false,
"grade_id": "cell-6d12164e3e76339b",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Question 3: redo for recipes"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"deletable": false,
"id": "hGpcMDickacN",
"nbgrader": {
"checksum": "6ccca5682cbed48c50d2a9ea4300b62c",
"grade": false,
"grade_id": "cell-a0618093f5920c44",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"### Implementation of `cooking_redo`\n",
"\n",
"def dependency_scheduler_cooking_redo(self, v):\n",
" \"\"\"Indicates that the task v needs to be redone, as something went bad.\n",
" This is the \"cooking\" version of the redo, in which the redo propagates\n",
" to both successors (as for code) and predecessors.\"\"\"\n",
" # YOUR CODE HERE\n",
" redo_tasks = {t} | self.successors[t]\n",
" self.completed_tasks = self.completed_tasks - redo_tasks\n",
" \n",
" return redo_tasks \n",
"\n",
"DependencyScheduler.cooking_redo = dependency_scheduler_cooking_redo\n"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"deletable": false,
"id": "Q-CPnQRjkacN",
"nbgrader": {
"checksum": "fef9951e6a653a4975154941aa5e881a",
"grade": false,
"grade_id": "cell-aa93a1ed6a926cff",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"# Here is a place where you can test your code. \n",
"\n",
"# YOUR CODE HERE"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "bGHcfSPikacN",
"nbgrader": {
"checksum": "27d265e4416ecc09bb1d88678feb3e3c",
"grade": false,
"grade_id": "cell-9a8d2fd5d7f7954d",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Let us check that the code works. First, a simple example. "
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 408
},
"deletable": false,
"editable": false,
"id": "jqvumL0KkacN",
"nbgrader": {
"checksum": "0ac9dcabcfdf363f540a35363e3c6103",
"grade": true,
"grade_id": "cell-c81c441e91888093",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
},
"outputId": "d81d41fa-d5aa-45ee-8b9f-97430299a424"
},
"outputs": [
{
"ename": "AssertionError",
"evalue": "Items in the second set but not the first:\n'b'\n'a'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 17\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcooking_redo\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'c'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 18\u001b[0m \u001b[1;31m# When we redo c, both its successor d, and predecessors a, b have to be redone.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 19\u001b[1;33m \u001b[0massert_equal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mavailable_tasks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'e'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 20\u001b[0m \u001b[0massert_equal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompleted_tasks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mset\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 21\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m~\\Anaconda3\\lib\\unittest\\case.py\u001b[0m in \u001b[0;36massertEqual\u001b[1;34m(self, first, second, msg)\u001b[0m\n\u001b[0;32m 837\u001b[0m \"\"\"\n\u001b[0;32m 838\u001b[0m \u001b[0massertion_func\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_getAssertEqualityFunc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfirst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msecond\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 839\u001b[1;33m \u001b[0massertion_func\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfirst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msecond\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mmsg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 840\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 841\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0massertNotEqual\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfirst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msecond\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m~\\Anaconda3\\lib\\unittest\\case.py\u001b[0m in \u001b[0;36massertSetEqual\u001b[1;34m(self, set1, set2, msg)\u001b[0m\n\u001b[0;32m 1097\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1098\u001b[0m \u001b[0mstandardMsg\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m'\\n'\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlines\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1099\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfail\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_formatMessage\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstandardMsg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 1100\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1101\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0massertIn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmember\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcontainer\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m~\\Anaconda3\\lib\\unittest\\case.py\u001b[0m in \u001b[0;36mfail\u001b[1;34m(self, msg)\u001b[0m\n\u001b[0;32m 678\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mfail\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 679\u001b[0m \u001b[1;34m\"\"\"Fail immediately, with the given message.\"\"\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 680\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfailureException\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 681\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 682\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0massertFalse\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mexpr\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mAssertionError\u001b[0m: Items in the second set but not the first:\n'b'\n'a'"
]
}
],
"source": [
"### Basic tests for `cooking_redo`. 10 points. \n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', [])\n",
"s.add_task('b', [])\n",
"s.add_task('c', ['a', 'b'])\n",
"s.add_task('d', ['c', 'a'])\n",
"s.add_task('e', [])\n",
"s.add_task('f', ['e'])\n",
"s.add_task('g', ['f', 'd'])\n",
"\n",
"s.mark_completed('a')\n",
"s.mark_completed('b')\n",
"s.mark_completed('c')\n",
"s.mark_completed('d')\n",
"assert_equal(s.available_tasks, {'e'})\n",
"s.cooking_redo('c')\n",
"# When we redo c, both its successor d, and predecessors a, b have to be redone.\n",
"assert_equal(s.available_tasks, {'a', 'b', 'e'})\n",
"assert_equal(s.completed_tasks, set())\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "wvmDn4kkkacN",
"nbgrader": {
"checksum": "9837d82e5252ea166f9df307ac050674",
"grade": false,
"grade_id": "cell-ca557220e8080773",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"And now, some slightly more sophisticated tests."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"deletable": false,
"editable": false,
"id": "7ICulRsskacN",
"nbgrader": {
"checksum": "e9305566644280e83a323332ac42b1f2",
"grade": true,
"grade_id": "cell-4c4d6c54901c7d9",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
}
},
"outputs": [
{
"ename": "AssertionError",
"evalue": "Items in the second set but not the first:\n'b'\n'a'",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 18\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcooking_redo\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'c'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 19\u001b[0m \u001b[1;31m# When we redo c, both its successor d, and predecessors a, b have to be redone.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 20\u001b[1;33m \u001b[0massert_equal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mavailable_tasks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'f'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 21\u001b[0m \u001b[0massert_equal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mcompleted_tasks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;34m'e'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 22\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m~\\Anaconda3\\lib\\unittest\\case.py\u001b[0m in \u001b[0;36massertEqual\u001b[1;34m(self, first, second, msg)\u001b[0m\n\u001b[0;32m 837\u001b[0m \"\"\"\n\u001b[0;32m 838\u001b[0m \u001b[0massertion_func\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_getAssertEqualityFunc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfirst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msecond\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 839\u001b[1;33m \u001b[0massertion_func\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mfirst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msecond\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mmsg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 840\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 841\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0massertNotEqual\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mfirst\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0msecond\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m~\\Anaconda3\\lib\\unittest\\case.py\u001b[0m in \u001b[0;36massertSetEqual\u001b[1;34m(self, set1, set2, msg)\u001b[0m\n\u001b[0;32m 1097\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1098\u001b[0m \u001b[0mstandardMsg\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;34m'\\n'\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlines\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m-> 1099\u001b[1;33m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfail\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_formatMessage\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mstandardMsg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 1100\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 1101\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0massertIn\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmember\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcontainer\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;32m~\\Anaconda3\\lib\\unittest\\case.py\u001b[0m in \u001b[0;36mfail\u001b[1;34m(self, msg)\u001b[0m\n\u001b[0;32m 678\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mfail\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 679\u001b[0m \u001b[1;34m\"\"\"Fail immediately, with the given message.\"\"\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 680\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfailureException\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mmsg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 681\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 682\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0massertFalse\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mexpr\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmsg\u001b[0m\u001b[1;33m=\u001b[0m\u001b[1;32mNone\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mAssertionError\u001b[0m: Items in the second set but not the first:\n'b'\n'a'"
]
}
],
"source": [
"### Advanced tests for `cooking_redo`. 10 points. \n",
"\n",
"s = DependencyScheduler()\n",
"s.add_task('a', [])\n",
"s.add_task('b', [])\n",
"s.add_task('c', ['a', 'b'])\n",
"s.add_task('d', ['c', 'a'])\n",
"s.add_task('e', [])\n",
"s.add_task('f', ['e'])\n",
"s.add_task('g', ['f', 'd'])\n",
"\n",
"s.mark_completed('a')\n",
"s.mark_completed('b')\n",
"s.mark_completed('c')\n",
"s.mark_completed('d')\n",
"s.mark_completed('e')\n",
"assert_equal(s.available_tasks, {'f'})\n",
"s.cooking_redo('c')\n",
"# When we redo c, both its successor d, and predecessors a, b have to be redone.\n",
"assert_equal(s.available_tasks, {'a', 'b', 'f'})\n",
"assert_equal(s.completed_tasks, {'e'})\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "k13cqyuJkacO",
"nbgrader": {
"checksum": "8e2d0c5ee7ca1ee6b0228ce1fcf4b129",
"grade": false,
"grade_id": "cell-1354768f9bcb0376",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"## Question 4: Implement And-Or Schedules\n",
"\n",
"In the schedules we have seen so far, the dependencies are in _and_ one with the other: if a task $a$ depends on $b, c$, then _both_ $b$ _and $c$ need to be completed before $a$ can be started. \n",
"It is possible to consider also cases where dependencies are in an _or_ relation: if $a$ depends on $b, c$ in an _or_ way, then it suffices to complete one of $b$ _or_ $c$ before starting $a$. \n",
"For instance, in our Carbonara Pasta example, it is possible (even though not necessarily advisable) to use shallots in place of onions. \n",
"In that case, instead of \n",
"\n",
" carbonara.add_task('put onions in pan', ['dice onions'])\n",
"\n",
"we could have:\n",
"\n",
" carbonara.add_or_task('put onions in pan', ['dice onions', 'dice shallots'])\n",
"\n",
"so that before putting the (now generally named) onions in a pan, we could choose to dice either shallots or onions. \n",
"\n",
"Formally, the idea is to endow the Scheduler class with _two_ methods: \n",
"\n",
"* `add_and_task(self, t, dependencies)` adds a task `t` with list of dependencies `dependencies`, so that `t` can be done when _all_ of the dependencies are done. The task `t` is called an AND node in the dependency graph. \n",
"\n",
"* `add_or_task(self, t, dependencies)` adds a task `t` with list of dependencies `dependencies`, so that `t` can be done when _at least one_ of the dependencies is done. The task `t` is called an OR node in the dependency graph. \n",
"\n",
"You need to find a way to remember which dependency graph nodes are AND or OR nodes, and you need to implement the properties `done`, `available_tasks`, `uncompleted`, and the method `mark_completed`, to make this class work. \n",
"Implementing the `show` method is optional; do it if it helps you debug your code. "
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"deletable": false,
"id": "MwsOh-7vkacO",
"nbgrader": {
"checksum": "b4902dcc720ffd415db259d5d7d9e70c",
"grade": false,
"grade_id": "cell-51732cbc3481ec67",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [
{
"ename": "IndentationError",
"evalue": "expected an indented block (, line 9)",
"output_type": "error",
"traceback": [
"\u001b[1;36m File \u001b[1;32m\"\"\u001b[1;36m, line \u001b[1;32m9\u001b[0m\n\u001b[1;33m def add_and_task(self, t, dependencies):\u001b[0m\n\u001b[1;37m ^\u001b[0m\n\u001b[1;31mIndentationError\u001b[0m\u001b[1;31m:\u001b[0m expected an indented block\n"
]
}
],
"source": [
"### `AND_OR_Scheduler` implementation\n",
"\n",
"class AND_OR_Scheduler(object):\n",
"\n",
" def __init__(self):\n",
" # It is up to you to implement the initialization.\n",
" # YOUR CODE HERE\n",
"\n",
" def add_and_task(self, t, dependencies):\n",
" \"\"\"Adds an AND task t with given dependencies.\"\"\"\n",
" # YOUR CODE HERE\n",
"\n",
" def add_or_task(self, t, dependencies):\n",
" \"\"\"Adds an OR task t with given dependencies.\"\"\"\n",
" # YOUR CODE HERE\n",
"\n",
" @property\n",
" def done(self):\n",
" # YOUR CODE HERE\n",
"\n",
" @property\n",
" def available_tasks(self):\n",
" \"\"\"Returns the set of tasks that can be done in parallel.\n",
" A task can be done if:\n",
" - It is an AND task, and all its predecessors have been completed, or\n",
" - It is an OR task, and at least one of its predecessors has been completed.\n",
" And of course, we don't return any task that has already been\n",
" completed.\"\"\"\n",
" # YOUR CODE HERE\n",
"\n",
" def mark_completed(self, t):\n",
" \"\"\"Marks the task t as completed, and returns the additional\n",
" set of tasks that can be done (and that could not be\n",
" previously done) once t is completed.\"\"\"\n",
" # YOUR CODE HERE\n",
"\n",
" def show(self):\n",
" \"\"\"You can use the nx graph to display the graph. You may want to ensure\n",
" that you display AND and OR nodes differently.\"\"\"\n",
" # YOUR CODE HERE\n"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"deletable": false,
"id": "Iol14ZOXkacO",
"nbgrader": {
"checksum": "27af5864ccc43f22993a72d0f647bdc4",
"grade": false,
"grade_id": "cell-038c005b93e6e0f1",
"locked": false,
"schema_version": 1,
"solution": true
}
},
"outputs": [],
"source": [
"# Here is a place where you can test your code. \n",
"\n",
"# YOUR CODE HERE"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "CPtCKfh4kacO",
"nbgrader": {
"checksum": "a5b1f35544aaaa7231a3bc093db3d5b9",
"grade": false,
"grade_id": "cell-482edb12036108dc",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Let us do some simple tests. First, for good old AND nodes. "
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"deletable": false,
"editable": false,
"id": "uwnhgFwekacO",
"nbgrader": {
"checksum": "c873af11920709a2d8333932e617be8b",
"grade": true,
"grade_id": "cell-9a86f02cc5bca274",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
}
},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'AND_OR_Scheduler' is not defined",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;31m### Simple tests for AND nodes. 10 points.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0ms\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mAND_OR_Scheduler\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd_and_task\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'c'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[0massert_equal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mavailable_tasks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'c'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mNameError\u001b[0m: name 'AND_OR_Scheduler' is not defined"
]
}
],
"source": [
"### Simple tests for AND nodes. 10 points. \n",
"\n",
"s = AND_OR_Scheduler()\n",
"s.add_and_task('a', ['b', 'c'])\n",
"assert_equal(s.available_tasks, {'b', 'c'})\n",
"r = s.mark_completed('b')\n",
"assert_equal(r, set())\n",
"assert_equal(s.available_tasks, {'c'})\n",
"r = s.mark_completed('c')\n",
"assert_equal(r, {'a'})\n",
"assert_equal(s.available_tasks, {'a'})\n",
"r = s.mark_completed('a')\n",
"assert_equal(r, set())\n",
"assert_equal(s.available_tasks, set())\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "_7OpH7p-kacO",
"nbgrader": {
"checksum": "451c1a9c2301ae0361c28ad6e69c35da",
"grade": false,
"grade_id": "cell-77803bc9d9342c22",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Then, some simple tests for OR nodes. "
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"deletable": false,
"editable": false,
"id": "6J8pACOTkacO",
"nbgrader": {
"checksum": "af51d6d7f9b58c91cc90e5e7c48a7647",
"grade": true,
"grade_id": "cell-19e1d2c726e4be1b",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
}
},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'AND_OR_Scheduler' is not defined",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;31m### Simple tests for OR nodes. 10 points.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0ms\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mAND_OR_Scheduler\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd_or_task\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'c'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[0massert_equal\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mavailable_tasks\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m{\u001b[0m\u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'c'\u001b[0m\u001b[1;33m}\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mNameError\u001b[0m: name 'AND_OR_Scheduler' is not defined"
]
}
],
"source": [
"### Simple tests for OR nodes. 10 points. \n",
"\n",
"s = AND_OR_Scheduler()\n",
"s.add_or_task('a', ['b', 'c'])\n",
"assert_equal(s.available_tasks, {'b', 'c'})\n",
"r = s.mark_completed('b')\n",
"# Now 'a' becomes available.\n",
"assert_equal(r, {'a'})\n",
"# But note that 'c' is also available, even if useless.\n",
"assert_equal(s.available_tasks, {'a', 'c'})\n",
"r = s.mark_completed('a')\n",
"assert_equal(r, set())\n",
"assert_equal(s.available_tasks, {'c'})\n",
"r = s.mark_completed('c')\n",
"assert_equal(r, set())\n",
"assert_equal(s.available_tasks, set())\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"deletable": false,
"editable": false,
"id": "wGCCs4GHkacO",
"nbgrader": {
"checksum": "587858ea7715ed0dfc82913fa3a63542",
"grade": false,
"grade_id": "cell-80257a5f8718f673",
"locked": true,
"schema_version": 1,
"solution": false
}
},
"source": [
"Note that a drawback of this simple solution, as illustrated by the above test case, is that we do not distinguish between the tasks that are useful to do the root task, and the tasks that are useless, that is, not part of a minimal solution. We simply call them available, as they can be done, even though there is no advantage in doing them. "
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"deletable": false,
"editable": false,
"id": "yqPCp5KWkacO",
"nbgrader": {
"checksum": "2da9553393518e5ca462fd18da9aa002",
"grade": true,
"grade_id": "cell-fa32f16841d80974",
"locked": true,
"points": 10,
"schema_version": 1,
"solution": false
}
},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'AND_OR_Scheduler' is not defined",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)",
"\u001b[1;32m\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[1;31m### Tests with both AND and OR nodes. 10 points.\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0ms\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mAND_OR_Scheduler\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 4\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd_and_task\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'a'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'c'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[0ms\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0madd_or_task\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'b'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;34m'b1'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'b2'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
"\u001b[1;31mNameError\u001b[0m: name 'AND_OR_Scheduler' is not defined"
]
}
],
"source": [
"### Tests with both AND and OR nodes. 10 points. \n",
"\n",
"s = AND_OR_Scheduler()\n",
"s.add_and_task('a', ['b', 'c'])\n",
"s.add_or_task('b', ['b1', 'b2'])\n",
"s.add_or_task('c', ['c1', 'c2'])\n",
"r = s.mark_completed('b1')\n",
"assert_equal(s.available_tasks, {'b', 'b2', 'c1', 'c2'})\n",
"r = s.mark_completed('b')\n",
"assert_false('a' in s.available_tasks)\n",
"r = s.mark_completed('c1')\n",
"assert_false('a' in s.available_tasks)\n",
"r = s.mark_completed('c')\n",
"assert_true('a' in s.available_tasks)\n",
"\n",
"s = AND_OR_Scheduler()\n",
"s.add_or_task('a', ['b', 'c'])\n",
"s.add_and_task('b', ['b1', 'b2'])\n",
"s.add_and_task('c', ['c1', 'c2'])\n",
"r = s.mark_completed('b1')\n",
"assert_equal(s.available_tasks, {'b2', 'c1', 'c2'})\n",
"r = s.mark_completed('c1')\n",
"assert_equal(s.available_tasks, {'b2', 'c2'})\n",
"r = s.mark_completed('c2')\n",
"assert_equal(s.available_tasks, {'b2', 'c'})\n",
"r = s.mark_completed('c')\n",
"assert_true('a' in s.available_tasks)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"colab": {
"collapsed_sections": [],
"name": "“Homework_11_Scheduling_with_Dependencies.ipynb”的副本",
"provenance": []
},
"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.1"
},
"test_info": {
"id": "4adb3d5055da02e7ae8251ca99f4acfa901ab256"
}
},
"nbformat": 4,
"nbformat_minor": 1
}
Assign/.ipynb_checkpoints/Solution file-checkpoint.ipynb{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Solution File"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Homework 11: Scheduling with Dependencies"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Our first implementation of the class is as follows. We let you complete the available_tasks and mark_completed methods"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"from collections import defaultdict\n",
"import networkx as nx # Library for displaying graphs.\n",
"import matplotlib.pyplot as plt\n",
"\n",
"class DependencyScheduler(object):\n",
"\n",
" def __init__(self):\n",
" self.tasks = set()\n",
" # The successors of a task are the tasks that depend on it, and can\n",
" # only be done once the task is completed.\n",
" self.successors = defaultdict(set)\n",
" # The predecessors of a task have to be done before the task.\n",
" self.predecessors = defaultdict(set)\n",
" self.completed_tasks = set() # completed tasks\n",
"\n",
" def add_task(self, t, dependencies):\n",
" \"\"\"Adds a task t with given dependencies.\"\"\"\n",
" # Makes sure we know about all tasks mentioned.\n",
" assert t not in self.tasks or len(self.predecessors[t]) == 0, \"The task was already present.\"\n",
" self.tasks.add(t)\n",
" self.tasks.update(dependencies)\n",
" # The predecessors are the tasks that need to be done before.\n",
" self.predecessors[t] = set(dependencies)\n",
" # The new task is a successor of its dependencies.\n",
" for u in dependencies:\n",
" self.successors[u].add(t)\n",
"\n",
" def reset(self):\n",
" self.completed_tasks = set()\n",
"\n",
" @property\n",
" def done(self):\n",
" return self.completed_tasks == self.tasks\n",
"\n",
"\n",
" def show(self):\n",
" \"\"\"We use the nx graph to display the graph.\"\"\"\n",
" g = nx.DiGraph()\n",
" g.add_nodes_from(self.tasks)\n",
" g.add_edges_from([(u, v) for u in self.tasks for v in self.successors[u]])\n",
" node_colors = ''.join([('g' if v in self.completed_tasks else 'r')\n",
" for v in self.tasks])\n",
" nx.draw(g, with_labels=True, node_color=node_colors)\n",
" plt.show()\n",
"\n",
" @property\n",
" def uncompleted(self):\n",
" \"\"\"Returns the tasks that have not been completed.\n",
" This is a property, so you can say scheduler.uncompleted rather than\n",
" scheduler.uncompleted()\"\"\"\n",
" return self.tasks - self.completed_tasks\n",
"\n",
" def _check(self):\n",
" \"\"\"We check that if t is a successor of u, then u is a predecessor\n",
" of t.\"\"\"\n",
" for u in self.tasks:\n",
" for t in self.successors[u]:\n",
" assert u in self.predecessors[t]\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Question 1: implement `available_tasks` and `mark_completed`. "
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"### Implementation of `available_tasks` and `mark_completed`.\n",
"\n",
"def scheduler_available_tasks(self):\n",
" \"\"\"Returns the set of tasks that can be done in parallel.\n",
" A task can be done if all its predecessors have been completed.\n",
" And of course, we don't return any task that has already been\n",
" completed.\"\"\"\n",
" # YOUR CODE HERE\n",
" return ({t for t in self.tasks \n",
" if self.predecessors[t].issubset(self.completed_tasks)}\n",
" - self.completed_tasks)\n",
"\n",
"def scheduler_mark_completed(self, t):\n",
" \"\"\"Marks the task t as completed, and returns the additional\n",
" set of tasks that can be done (and that could not be\n",
" previously done) once t is completed.\"\"\"\n",
" # YOUR CODE HERE\n",
" for p in...
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here