{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "MidTermNotes.ipynb",
      "provenance": [],
      "collapsed_sections": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "brNDGX-kvzds"
      },
      "source": [
        "# Template for Notes for CIS 521 Midterm 1\n",
        "\n",
        "The midterm exam for CIS 521 will be **open notes**. No other matierals are allowed for the exam - you may not use the textbook, the internet, a Python envionement, or communicate with anyone during the exam. \n",
        "\n",
        "This python notebook is where you should save all of your notes.  It provides a structured template that allows you to add summaries for each of the textbook sections that are part of the required reading, plus room for lecture notes and definitions.\n",
        "\n",
        "Your summaries must be written in your own words.  Do not copy and paste from the textbook.  Do not share your notes with classmates, or copy any of your classmates' notes.  "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aPtM8g7R0Pds"
      },
      "source": [
        "# Instructions\n",
        "\n",
        "1. Make a copy of this Google Colab file. From the colab menu, pick **File > Save a copy in Drive**.  That will save a copy of this notebook in your Google drive.\n",
        "2. Go through each section of the notebook, and fill in all the summaries for each chapter and its corresponding sections.  Be sure to save your work frequently!\n",
        "3. Read the notes below about how you can format your summaries.\n",
        "4. In addition to these notes, you are allowed 1 page (both sides) for figures or handwritten notes.\n",
        "5. Submit your version of this notebook to Gradescope before you start the exam.\n",
        "6. If you are taking the exam in person, then you must print a copy of your notes for yourself.   Here's how to print: \n",
        "* Run the last cell of this notebook\n",
        "* Click the \"Mirror cell in tab\" button (this is located in the upper right corner of the cell, immediately to the right of the trash can).  This will open the cell.  \n",
        "* In Chrome, you can then print your notes using the **File > Print** option from the browser's menu.  \n",
        "* In the print options, change the layout from Landscape to Portrait. \n",
        "* Clicking the Save, will save a PDF that you can then print.\n",
        "\n",
        "## Formatting Notes\n",
        "\n",
        "1. The max length for each section's summary is 500 words / 3000 characters.\n",
        "2. You can also write a 2000 word summary of the lectures for each module.\n",
        "3. You may also write definitions of concepts for each module.  The definitions should be written in your own words (not copy and pasted), and they should be no more than 20 words long for each concept. \n",
        "2. You can use Markdown formatting for your summaries. \n",
        "3. You can use Latex formatting for equations. Put two dollar signs before and after your equation like this: \n",
        "$$ \\epsilon = \\frac{1}{mc^2} $$\n",
        "4. When you write python code or psuedocode algorithms, you should surround it with triple backticks like this:\n",
        "```\n",
        "def function():\n",
        "      print('hello world')\n",
        "```\n",
        "5. Write \\ whenever you want to keep writing on the next line without a newline being inserted."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cw5IpDLCZa4W"
      },
      "source": [
        "## Example summary\n",
        "\n",
        "This is a sample summary of [Section 2.1.1](https://web.stanford.edu/~jurafsky/slp3/2.pdf ) of Jurfasky and Martin's Speech and Language Processing's book\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "lzaavW96tcxM",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "20793966-34f6-49e7-c785-d80c79225b27"
      },
      "source": [
        "chapter_2_1_example = \"\"\"\n",
        "Regular expressions are an algebraic notation for characterizing \\\n",
        "a set of strings that are useful for searching for a pattern or through a corpus.\\\n",
        "Only the first matching result will be shown. The search string is case sensitive, \\\n",
        "but it can consist of a single character or a sequence of characters. \\\n",
        "While square braces specify a disjunction of characters, the dash can more easily look \\\n",
        "for characters over a range. The caret specifies the non-existence of a character. \\\n",
        "Additionally, the (?) is used to for flexibility on a single character, meaning \\\n",
        "\"the preceding character or nothing\". More importantly, the Kleene star means zero or \\\n",
        "more occurrences of the immediately previous character or regular expression in square braces.\\\n",
        "...\n",
        "\"\"\"\n",
        "\n",
        "length = len(chapter_2_1_example.split(\" \"))\n",
        "print(\"The example summary is {len} words long, so you could write an additional \\\n",
        "{remaining} words.\".format(len=length, remaining=(500-length)))"
      ],
      "execution_count": 40,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "The example summary is 116 words long, so you could write an additional 384 words.\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s3Uk9gQTevcj"
      },
      "source": [
        "## Example summary length\n",
        "\n",
        "The maximum summary length for each section is 500 words / 3000 characters.  Hopefully you'll find that to be quite generous. "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "8DvZ57A-djo1",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "5771c622-b0c5-4b67-e9ab-0c9010c22dc6"
      },
      "source": [
        "# This is an example of a 500 word placeholder\n",
        "placeholder = \"\"\"ultrices vitae auctor eu augue ut lectus arcu bibendum at varius \\\n",
        " vel pharetra vel turpis nunc eget lorem dolor sed viverra ipsum nunc aliquet \\\n",
        " bibendum enim facilisis gravida neque convallis a cras semper auctor neque vitae \\\n",
        " tempus quam pellentesque nec nam aliquam sem et tortor consequat id porta nibh \\\n",
        " venenatis cras sed felis eget velit aliquet sagittis id consectetur purus ut \\\n",
        " faucibus pulvinar elementum integer enim neque volutpat ac tincidunt vitae semper \\ \n",
        " quis lectus nulla at volutpat diam ut venenatis tellus in metus vulputate eu \\\n",
        " scelerisque felis imperdiet proin fermentum leo vel orci porta non pulvinar neque \\ \n",
        " laoreet suspendisse interdum consectetur libero id faucibus nisl tincidunt eget \\\n",
        " nullam non nisi est sit amet facilisis magna etiam tempor orci eu lobortis \\\n",
        " elementum nibh tellus molestie nunc non blandit massa enim nec dui nunc mattis \\\n",
        " enim ut tellus elementum sagittis vitae et leo duis ut diam quam nulla porttitor \\\n",
        " massa id neque aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus \\\n",
        " imperdiet nulla malesuada pellentesque elit eget gravida cum sociis natoque \\\n",
        " penatibus et magnis dis parturient montes nascetur ridiculus mus mauris vitae \\\n",
        " ultricies leo integer malesuada nunc vel risus commodo viverra maecenas accumsan \\\n",
        " lacus vel facilisis volutpat est velit egestas dui id ornare arcu odio ut sem nulla \\\n",
        " pharetra diam sit amet nisl suscipit adipiscing bibendum est ultricies integer \\\n",
        " quis auctor elit sed vulputate mi sit amet mauris commodo quis imperdiet massa \\\n",
        " tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada proin libero nunc \\\n",
        " consequat interdum varius sit amet mattis vulputate enim nulla aliquet porttitor \\\n",
        " lacus luctus accumsan tortor posuere ac ut consequat semper viverra nam libero \\\n",
        " justo laoreet sit amet cursus sit amet dictum sit amet justo donec enim diam \\\n",
        " vulputate ut pharetra sit amet aliquam id diam maecenas ultricies mi eget mauris \\\n",
        " pharetra et ultrices neque ornare aenean euismod elementum nisi quis eleifend quam \\\n",
        " adipiscing vitae proin sagittis nisl rhoncus mattis rhoncus urna neque viverra justo \\\n",
        " nec ultrices dui sapien eget mi proin sed libero enim sed faucibus turpis in \\\n",
        " eu mi bibendum neque egestas congue quisque egestas diam in arcu cursus euismod \\\n",
        " quis viverra nibh cras pulvinar mattis nunc sed blandit libero volutpat sed cras \\\n",
        " ornare arcu dui vivamus arcu felis bibendum ut tristique et egestas quis ipsum \\\n",
        " suspendisse ultrices gravida dictum fusce ut placerat orci nulla pellentesque \\\n",
        " dignissim enim sit amet venenatis urna cursus eget nunc scelerisque viverra mauris \\\n",
        " in aliquam sem fringilla ut morbi tincidunt augue interdum velit euismod in \\\n",
        " pellentesque massa placerat duis ultricies lacus sed turpis tincidunt id aliquet \\\n",
        " risus feugiat in ante metus dictum at tempor commodo ullamcorper a lacus vestibulum \\\n",
        " sed arcu non odio euismod lacinia at quis risus sed vulputate odio ut enim blandit \\\n",
        " volutpat maecenas volutpat blandit aliquam etiam erat velit scelerisque in \\\n",
        " dictum non consectetur a erat nam at lectus urna\"\"\"\n",
        "\n",
        "print(\"The length in words is\", len(placeholder.split(' ')))\n",
        "print(\"The length in characters is\", len(placeholder))"
      ],
      "execution_count": 41,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "The length in words is 500\n",
            "The length in characters is 3030\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5yG3-YncJfTB"
      },
      "source": [
        "# Permissions to use your summaries\n",
        "\n",
        "We'd like to use your summaries to help develop better educational resources like [remember.school](https://remember.school) or a question-answering system about the textbook.  \n",
        "\n",
        "1. Please enter your name.  \n",
        "\n",
        "2. If you grant permission for us to use your summaries under a [Creative Commons License](https://creativecommons.org/licenses/by/4.0/), please set ```permission = True```.  If you do not grant permission, please set it to false. Your answer won't affect your grade.\n",
        "\n",
        "3. If you grant us permission, and you'd like to remain anonymous, set ```anonymous = True```.  We'll remove your name before sharing your summaries with anyone. "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "2RECnJArJew4"
      },
      "source": [
        "name = \"\"\n",
        "permission = True\n",
        "anonymous = False"
      ],
      "execution_count": 42,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GZZXyrV-xuAp"
      },
      "source": [
        "# Module 1 - Intro to AI"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Y2NfE1CKxzS-"
      },
      "source": [
        "# Russell and Norvig, AIMA 4th Edition, Chapter 1 “Introduction”, Section 1.1 “What is AI”\n",
        "\n",
        "chapter1_1 = \"\"\" Chapter 1.1 summary. \n",
        "\n",
        "AI is defined in two distinct ways : \n",
        "Intelligence can be defined in terms of fidelity to humans performance. (machine imitating humans).\n",
        "There is another abstract definition of intelligence - called acting rationally. \n",
        "These results in two fold dichotomy \n",
        "Human vs Rational\n",
        "Thought vs Behaviour\n",
        "\n",
        "Six disciplines of AI are:\n",
        "1. NLP\n",
        "2. Knowledge representation\n",
        "3. Automated reasoning\n",
        "4. Machine learning\n",
        "5. Total Turing test\n",
        "    5.1 Computer vision\n",
        "    5.2 Robotics\n",
        "The quest for artifical flight succeeded when engineer and inventors stopped immitating \\\n",
        "bird and started using wind tunnels and learning about aerodynamics.\\\n",
        "Rational Agent : An agent that acts to achieve the best outcome or the best expected outcome if uncertainity is involved.\n",
        "\"\"\" \n",
        "\n",
        "\n",
        "# Russell and Norvig, AIMA 4th Edition, Chapter 27 “Philosophical Foundations”, Sections 27.1 “The Limits of AI” and 27.2 “Can Machines Really Think?”\n",
        "chapter27_1 = \"\"\" The Limits of AI - \n",
        "\n",
        "1. Weak AI - the idea that machines could act as if they were intelligent\n",
        "2. Strong AI - the assertion that machines that do so are actually comsciously thinking (not just simulating thinking) \n",
        "This has morphed into human level AI or general AI - prgrams that can solve an arbitrarily wide variety of tasks.\n",
        "\"\"\"\n",
        "chapter27_2 = \"\"\" Can Machines really think - Computer are expected to simulate thinking.\n",
        "Do computers really have consciousness - are they self aware and aware of the outside world - are they aware of their own mortality or lack thereof.\n",
        "Do computers have qualia - the subjective experience of learning. Humans have a clear advantage over machines - hey can use their own subjective aparatus \\\n",
        "to appreciate the subjective experience of others. Machine however can run each others code.\n",
        "\n",
        " \"\"\"\n",
        "\n",
        "# Russell and Norvig, AIMA 4th Edition, Chapter 2 “Intelligent Agents” (whole chapter)\n",
        "chapter2_1 = \"\"\" A agent percieves its environment and acts upon that environment throught actuators. \"\"\"\n",
        "chapter2_2 = \"\"\" Rationality - doing the right things. \n",
        "Consequentialism - evaluating an agents behavior by its consequences. The sequence of actions causes the environment to go through a sequence of states. \\\n",
        "if the sequence is desirable, then the agent has performed well. This notion of desirability is captured by a performance measure.\\\n",
        "What is rational at any point depents on \\\n",
        "1. Performance measure defining the criteria for success.\n",
        "2. The agents prior knowledge of the environment\n",
        "3. The actions that the agent can perform\n",
        "4. The agents percept sequence to date.\n",
        "\"\"\"\n",
        "\n",
        "chapter2_3 = \"\"\" Your summary here \"\"\"\n",
        "chapter2_4 = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "# Lecture notes \n",
        "module1_lecture_notes = \"\"\" An agent is anything that can be viewed as\n",
        "• perceiving its environment through sensors and • acting upon that environment through actuators\n",
        "Human agent:\n",
        "• Sensors: eyes, ears, ...\n",
        "• Actuators: hands, legs, mouth, ...\n",
        "Robotic agent:\n",
        "• Sensors: cameras and infrared range finders \n",
        "• Actuators: various motors\n",
        "Agents include humans, robots, softbots, thermostats, ...\n",
        "\n",
        "Rational Agent:\n",
        "• For each possible percept sequence P\n",
        "• a rational agent selects an action a\n",
        "• to maximize its performance measure\n",
        " For each possible percept sequence P,\n",
        "• a rational agent selects an action a\n",
        "• to maximize its performance measure\n",
        "  Rational Agent (revised definition):\n",
        "• For each possible percept sequence P,\n",
        "• a rational agent selects an action a\\\n",
        "• that maximizes the expected value of its performance measure\n",
        "To design a rational agent we need to specify a task environment\n",
        "• a problem specification for which the agent is a solution\n",
        "PEAS: to specify a task environment • Performance measure\n",
        "• Environment\n",
        "• Actuators\n",
        "• Sensors\n",
        " \"\"\"\n",
        "\n",
        "\n",
        "# Definitions of concepts from Module 1.\n",
        "# You can define any bolded term in the textbook, \n",
        "# but you most use your own words for the definitions.\n",
        "# Each definition should be at most 20 words long.\n",
        "module1_definitions = {\n",
        "\"agent\" : \"anything that can perceive and act in an environment\",\n",
        "\"percept\" : \"Content that an agent's sensor are receiving\",\n",
        "\"percept sequence\" : \"is the complete history of everything the agent has ever received.\",\n",
        "\"agent function\" : \"the agent function maps any given percept sequence to an action.\",\n",
        "\"agent program:\": \" The agent functon will be implemented by an agent program - however they are distinct agent function is  an abstract mathematical description.\\\n",
        "The agent program is a concrete implmentation running in some physical system.\",\n",
        "\n",
        "\"omniscience\" : \"An omniscience agent knows the actual outcome of its actions and can act accordingly; but omniscience is actually impossible in reality.\",\n",
        "\"information gathering\": \"Taking effective actions to modify future percepts is called information gathering\",\n",
        "\"Learning\": \"Information gathering is not enough, the agent needs to learn as much as possible from what it perceives. The agent's initial configuration could\\\n",
        "reflect some prior knowledge of the environment, but as the agent gains more experience, this maybe modified and augmented.\",\n",
        "\"autonomy\": \"The virtue of an rational agent to be self sufficient by  learning what it can to compensate for partial or incorrect prior knowledge.\",\n",
        "\"EPISODIC\": \"The agents experience is divided into atomic episodes - crucially the next episode does not depend on the actions taken in previous episodes\",\n",
        "\"SEQUENTIAL\":\"In sequential TE, the current decision will impact all other furture decisions.\",\n",
        "\"DYNAMIC\": \"If the environment can change whie the agent is deliberating\",\n",
        "\"STATIC\": \"The environment is persisted in the same state while the agent is deliberating\",\n",
        "\"SEMI-DYNAMIC\":\"Environment itself does not change with the passage of time whilst the agent is deliberating but the agents performance score does.\",\n",
        "}\n",
        "\n"
      ],
      "execution_count": 43,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "elCCgY64spuq"
      },
      "source": [
        "## Preview of your notes for Module 1\n",
        "\n",
        "Here's a preview of your how your notes will be displayed.  At the end of this Python notebook, we'll aggregate your notes for all modules together into a single display.\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "gYYeNcdM_maE",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "outputId": "98fd59ee-b416-4939-d4cc-507d2a644c3f"
      },
      "source": [
        "#@title Preview of your summary\n",
        "#@markdown You don't need to modify anything in this cell. Just press play.\n",
        "from IPython.display import display, Markdown, Latex\n",
        "\n",
        "module1_sections = [\n",
        "  (\"AIMA Chapter 1.1 What is AI?\", chapter1_1),\n",
        "  (\"AIMA Chapter 27.1 The Limits of AI\", chapter27_1),\n",
        "  (\"AIMA Chapter 27.1 Can Machines Really Think?\", chapter27_2),\n",
        "  (\"AIMA 2.1 Agents and Environments\", chapter2_1),\n",
        "  (\"AIMA 2.2 Good Behavior: The Concept of Rationality\", chapter2_2),\n",
        "  (\"AIMA 2.3 The Nature of Environments\", chapter2_3),\n",
        "  (\"AIMA 2.4 The Structure of Agents\", chapter2_4),\n",
        "  (\"Module 1 Lecture Notes\", module1_lecture_notes),\n",
        "]\n",
        "\n",
        "\n",
        "def length_check(sections):\n",
        "  warnings = \"\"\n",
        "  for section, summary in sections:\n",
        "    max_word_length = 500\n",
        "    max_character_length = 3000\n",
        "    if \"lecture\" in section.lower():\n",
        "      # It's OK to do longer summaries of the lectures\n",
        "      max_word_length = 2000\n",
        "      max_character_length = 12000  \n",
        "    if (len(summary) > max_character_length or len(summary.split(' ')) > max_word_length):\n",
        "      warnings += \"WARNING: Your summary of '{section}' is too long!\\n\".format(section=section)\n",
        "  return warnings\n",
        "\n",
        "def length_check_definitions(definitions):\n",
        "  warnings = \"\"\n",
        "  for concept, definition in definitions.items():\n",
        "    if(len(definition.split(' ')) > 20):\n",
        "      warnings += \"WARNING: Your definition of '{concept}' is too long!\\n\".format(concept=concept)\n",
        "  return warnings\n",
        "\n",
        "\n",
        "def format_module(module_num, title, sections, definitions, show_placeholder=False):\n",
        "  markdown = \"\"\n",
        "\n",
        "  # Check summary lengths\n",
        "  warnings = length_check(sections)\n",
        "  if warnings != \"\":\n",
        "    markdown += \"## \" + warnings\n",
        "\n",
        "  # Check definition lengths\n",
        "  warnings = length_check_definitions(definitions)\n",
        "  if warnings != \"\":\n",
        "    markdown += \"## \" + warnings\n",
        "\n",
        "  markdown += \"# Module {num}: {title}\\n\".format(num=module_num, title=title)\n",
        "\n",
        "\n",
        "  for section, summary in sections:\n",
        "    if show_placeholder and summary == \" Your summary here \":\n",
        "      summary = placeholder\n",
        "    markdown += \"## {section}\\n{summary}\\n\\n\".format(section=section, summary=summary)\n",
        "  \n",
        "  markdown += \"## Module {num} Definitions\\n\".format(num=module_num)\n",
        "  for concept, definition in definitions.items():\n",
        "    markdown += \"* **{concept}** - {definition}\\n\".format(concept=concept.capitalize(), definition=definition)\n",
        "  return markdown\n",
        "\n",
        "\n",
        "module1_markdown = format_module(1, \"Rational Agents and Task Environments\", module1_sections, module1_definitions)\n",
        "display(Markdown(module1_markdown))"
      ],
      "execution_count": 44,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "## WARNING: Your definition of 'agent program:' is too long!\nWARNING: Your definition of 'omniscience' is too long!\nWARNING: Your definition of 'Learning' is too long!\nWARNING: Your definition of 'autonomy' is too long!\nWARNING: Your definition of 'EPISODIC' is too long!\nWARNING: Your definition of 'SEMI-DYNAMIC' is too long!\n# Module 1: Rational Agents and Task Environments\n## AIMA Chapter 1.1 What is AI?\n Chapter 1.1 summary. \n\nAI is defined in two distinct ways : \nIntelligence can be defined in terms of fidelity to humans performance. (machine imitating humans).\nThere is another abstract definition of intelligence - called acting rationally. \nThese results in two fold dichotomy \nHuman vs Rational\nThought vs Behaviour\n\nSix disciplines of AI are:\n1. NLP\n2. Knowledge representation\n3. Automated reasoning\n4. Machine learning\n5. Total Turing test\n    5.1 Computer vision\n    5.2 Robotics\nThe quest for artifical flight succeeded when engineer and inventors stopped immitating bird and started using wind tunnels and learning about aerodynamics.Rational Agent : An agent that acts to achieve the best outcome or the best expected outcome if uncertainity is involved.\n\n\n## AIMA Chapter 27.1 The Limits of AI\n The Limits of AI - \n\n1. Weak AI - the idea that machines could act as if they were intelligent\n2. Strong AI - the assertion that machines that do so are actually comsciously thinking (not just simulating thinking) \nThis has morphed into human level AI or general AI - prgrams that can solve an arbitrarily wide variety of tasks.\n\n\n## AIMA Chapter 27.1 Can Machines Really Think?\n Can Machines really think - Computer are expected to simulate thinking.\nDo computers really have consciousness - are they self aware and aware of the outside world - are they aware of their own mortality or lack thereof.\nDo computers have qualia - the subjective experience of learning. Humans have a clear advantage over machines - hey can use their own subjective aparatus to appreciate the subjective experience of others. Machine however can run each others code.\n\n \n\n## AIMA 2.1 Agents and Environments\n A agent percieves its environment and acts upon that environment throught actuators. \n\n## AIMA 2.2 Good Behavior: The Concept of Rationality\n Rationality - doing the right things. \nConsequentialism - evaluating an agents behavior by its consequences. The sequence of actions causes the environment to go through a sequence of states. if the sequence is desirable, then the agent has performed well. This notion of desirability is captured by a performance measure.What is rational at any point depents on 1. Performance measure defining the criteria for success.\n2. The agents prior knowledge of the environment\n3. The actions that the agent can perform\n4. The agents percept sequence to date.\n\n\n## AIMA 2.3 The Nature of Environments\n Your summary here \n\n## AIMA 2.4 The Structure of Agents\n Your summary here \n\n## Module 1 Lecture Notes\n An agent is anything that can be viewed as\n• perceiving its environment through sensors and • acting upon that environment through actuators\nHuman agent:\n• Sensors: eyes, ears, ...\n• Actuators: hands, legs, mouth, ...\nRobotic agent:\n• Sensors: cameras and infrared range finders \n• Actuators: various motors\nAgents include humans, robots, softbots, thermostats, ...\n\nRational Agent:\n• For each possible percept sequence P\n• a rational agent selects an action a\n• to maximize its performance measure\n For each possible percept sequence P,\n• a rational agent selects an action a\n• to maximize its performance measure\n  Rational Agent (revised definition):\n• For each possible percept sequence P,\n• a rational agent selects an action a• that maximizes the expected value of its performance measure\nTo design a rational agent we need to specify a task environment\n• a problem specification for which the agent is a solution\nPEAS: to specify a task environment • Performance measure\n• Environment\n• Actuators\n• Sensors\n \n\n## Module 1 Definitions\n* **Agent** - anything that can perceive and act in an environment\n* **Percept** - Content that an agent's sensor are receiving\n* **Percept sequence** - is the complete history of everything the agent has ever received.\n* **Agent function** - the agent function maps any given percept sequence to an action.\n* **Agent program:** -  The agent functon will be implemented by an agent program - however they are distinct agent function is  an abstract mathematical description.The agent program is a concrete implmentation running in some physical system.\n* **Omniscience** - An omniscience agent knows the actual outcome of its actions and can act accordingly; but omniscience is actually impossible in reality.\n* **Information gathering** - Taking effective actions to modify future percepts is called information gathering\n* **Learning** - Information gathering is not enough, the agent needs to learn as much as possible from what it perceives. The agent's initial configuration couldreflect some prior knowledge of the environment, but as the agent gains more experience, this maybe modified and augmented.\n* **Autonomy** - The virtue of an rational agent to be self sufficient by  learning what it can to compensate for partial or incorrect prior knowledge.\n* **Episodic** - The agents experience is divided into atomic episodes - crucially the next episode does not depend on the actions taken in previous episodes\n* **Sequential** - In sequential TE, the current decision will impact all other furture decisions.\n* **Dynamic** - If the environment can change whie the agent is deliberating\n* **Static** - The environment is persisted in the same state while the agent is deliberating\n* **Semi-dynamic** - Environment itself does not change with the passage of time whilst the agent is deliberating but the agents performance score does.\n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9xRbdqDox2zD"
      },
      "source": [
        "#Module 2 - Python"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "DpBvu7EMx7sy"
      },
      "source": [
        "# No readings for this module.  Instead you can summarize the lecture instead.\n",
        "\n",
        "# Lecture notes \n",
        "module2_lecture_notes = \"\"\" \n",
        "Functional level profiling:\n",
        "o Rudimentary \n",
        "import time\n",
        "t0 = time.time() \n",
        "code_block\n",
        "t1 = time.time() \n",
        "total = t1-t0\n",
        "\n",
        "o Timeit (more precise) \n",
        "import timeit\n",
        "t = timeit.Timer(\"<statement to time>\", \"<setup code>\") \n",
        "t.timeit()\n",
        "▪ The second argument is usually an import that sets up a virtual environment for the statement\n",
        "▪ timeit calls the statement 1 million times and returns the total elapsed time, number argument specifies number of times to run it.\n",
        "\n",
        "Script level profiling \n",
        "\n",
        "python -m cProfile to_time.py\n",
        "\"\"\"\n",
        "\n",
        "# Each definition should be at most 20 words long.\n",
        "module2_definitions = {\n",
        "\"mutability\" : \"In Python, 'mutable' is the ability of objects to change their values.\",\n",
        "\"mutable objects\" : \"Lists,Sets,Dictionaries,User-Defined Classes\",\n",
        "\"immutable objects\": \"Numbers (Integer, Rational, Float, Decimal, Complex & Booleans)Strings,Tuples,Frozen Sets,User-Defined Classes\",\n",
        "\"Important\":\"#tuples are immutable, so you cannot add new elements, hence, using merge of tuples with the #\",\n",
        "\"Code1\": \"operator to add a new imaginary day in the tuple ‘weekdays’\",\n",
        "\"Code2\": \"weekdays = ‘Sunday’, ‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Thursday’, ‘Friday’, ‘Saturday’\",\n",
        "\"Code3\": \"weekdays  +=  ‘Pythonday’\",\n",
        "\"horder 1\" : \"def is_even(x): return x % 2 == 0\",\n",
        "\"horder 2\" : \"[i for i in [1, 2, 3, 4, 5] if is_even(i)]\\\n",
        "[2, 4]\",\n",
        "\"horder 3\": \"list(filter(is_even, [1, 2, 3, 4, 5])) #equivalent [2, 4]\",\n",
        "\"horder 4\":\"list(filter(lambda x: x%2==0, [1, 2, 3, 4, 5])) #also equivalent\",\n",
        "\"complex loop\": \"for (x, y) in [('a',1), ('b',2), ('c',3), ('d',4)]:\",\n",
        "\"complex loop1\":\"             print(x)\",\n",
        "\"subclasses\" :\"class AI_Student(Student):\",\n",
        "\"subclasses2\" : \"fav_class = CIS 521\",\n",
        "\"list comprehension\": \" squares = [x*x for x in nums]\",\n",
        "\"list comprehension with filter\": \" li = [3, 6, 2, 7, 1, 9];[elem * 2 for elem in li if elem > 4] #[12, 14, 18]\",\n",
        "\"dictionary comprehension\": \"dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}; # Check for items greater than 2 ;dict1_cond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0}\",\n",
        "\"dictionary comprehension2\":\"dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6} # Identify odd and even entries#dict1_tripleCond = {k:('even' if v%2==0 else 'odd') for (k,v) in dict1.items()}\",\n",
        "\"nested dictionary comp\": \"nested_dict = {'first':{'a':1}, 'second':{'b':2}}#float_dict = {outer_k: {float(inner_v) for (inner_k, inner_v) in outer_v.items()} for (outer_k, outer_v) in nested_dict.items()}#print(float_dict)\",\n",
        "\"list operations\":\"append(value), index(value), remove(value), count(value), a.reverse(), a.sort, b_list = sorted(a)\",\n",
        "\"set operations\":\"add, remove, discard(silent), clear,pop,UNION x1.union(x2) or x1 | x2, INTERSECTION: x1.intersection(x2) or x1 & x2, MINUS x1.difference(x2) or x1 - x2\",\n",
        "\"set operations2\":\"Update x1.update(x1,x2...),SYMMETRIC difference :x1.symmetric_difference(x2) or x1^x2\",\n",
        "\"invalid set operations1\" : \"a = [1, 2, 3]; {a} # TypeError: unhashable type: 'list'\",\n",
        "\"invalid set operations2\" : \"d = {'a': 1, 'b': 2}; {d} TypeError: unhashable type: 'dict'\",\n",
        "\"dict operations\":\"dict.keys(), dict.values(), dict[key] = value\",\n",
        "\"frozen_set\":\"frozen sets are immutable x = frozenset(['foo', 'bar', 'ba'])\",\n",
        "\"__init__\": \"(duck typing) The constructor for the class.\",\n",
        "\"__len__\": \"Define how len( obj ) works.\",\n",
        "\"__copy__\": \"Define how to copy a class.\",\n",
        "\"__cmp__\" : \"Define how == works for class.\",\n",
        "\"__add__\" : \"Define how + works for class\",\n",
        "\"__neg__\" : \"Define how unary negation works for class\",\n",
        "\"__iter__\": \"Denotes if an oject is iterable iterable: __iter__ method which returns an iterator obj\",\n",
        "\"__next__\": \"Denotes if the object is a iterator : returns the next object in the sequence\",\n",
        "\"generators\": \"Generators are iterators (with __next()__ method)\",\n",
        "\"generators key benefits\" :\"▪ Less code than writing a standard iterator\\\n",
        "▪ Maintains local state automatically\\\n",
        "▪ Values are computed one at a time, as they’re needed\\\n",
        "▪ Avoids storing the entire sequence in memory\\\n",
        "▪ Good for aggregating (summing, counting) items. One pass.\\\n",
        "▪ Crucial for infinite sequences\\\n",
        "▪ Bad if you need to inspect the individual values\",\n",
        "\"yield\": \"Functions that contain the yield keyword automatically return a generator when called\",\n",
        "\"generators are better1\":\"Review: generators are good for aggregating items.\",\n",
        "\"generators are better2\":\"sum(x for x in xrange(10**8) if x%5==0) #999999950000000L (Generator)\",\n",
        "\"generators are better3\":\"Than version this which creates the whole list first\",\n",
        "\"generators are better4\":\"sum([x for x in xrange(10**8) if x%5==0]) 999999950000000L (List comprehension)\",\n",
        "\n",
        "}"
      ],
      "execution_count": 45,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ydOK__lwWgdp",
        "cellView": "form",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "outputId": "0210ca93-0d7e-4771-f23e-949505dec1fa"
      },
      "source": [
        "#@title Preview of your summary\n",
        "#@markdown You don't need to modify anything in this cell. Just press play.\n",
        "\n",
        "module2_sections = [\n",
        "  (\"Module 2 Lecture Notes\", module2_lecture_notes),\n",
        "]\n",
        "\n",
        "module2_markdown = format_module(2, \"Python\", module2_sections, module2_definitions)\n",
        "display(Markdown(module2_markdown))"
      ],
      "execution_count": 46,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "## WARNING: Your definition of 'list comprehension with filter' is too long!\nWARNING: Your definition of 'dictionary comprehension' is too long!\nWARNING: Your definition of 'dictionary comprehension2' is too long!\nWARNING: Your definition of 'set operations' is too long!\nWARNING: Your definition of 'generators key benefits' is too long!\n# Module 2: Python\n## Module 2 Lecture Notes\n \nFunctional level profiling:\no Rudimentary \nimport time\nt0 = time.time() \ncode_block\nt1 = time.time() \ntotal = t1-t0\n\no Timeit (more precise) \nimport timeit\nt = timeit.Timer(\"<statement to time>\", \"<setup code>\") \nt.timeit()\n▪ The second argument is usually an import that sets up a virtual environment for the statement\n▪ timeit calls the statement 1 million times and returns the total elapsed time, number argument specifies number of times to run it.\n\nScript level profiling \n\npython -m cProfile to_time.py\n\n\n## Module 2 Definitions\n* **Mutability** - In Python, 'mutable' is the ability of objects to change their values.\n* **Mutable objects** - Lists,Sets,Dictionaries,User-Defined Classes\n* **Immutable objects** - Numbers (Integer, Rational, Float, Decimal, Complex & Booleans)Strings,Tuples,Frozen Sets,User-Defined Classes\n* **Important** - #tuples are immutable, so you cannot add new elements, hence, using merge of tuples with the #\n* **Code1** - operator to add a new imaginary day in the tuple ‘weekdays’\n* **Code2** - weekdays = ‘Sunday’, ‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Thursday’, ‘Friday’, ‘Saturday’\n* **Code3** - weekdays  +=  ‘Pythonday’\n* **Horder 1** - def is_even(x): return x % 2 == 0\n* **Horder 2** - [i for i in [1, 2, 3, 4, 5] if is_even(i)][2, 4]\n* **Horder 3** - list(filter(is_even, [1, 2, 3, 4, 5])) #equivalent [2, 4]\n* **Horder 4** - list(filter(lambda x: x%2==0, [1, 2, 3, 4, 5])) #also equivalent\n* **Complex loop** - for (x, y) in [('a',1), ('b',2), ('c',3), ('d',4)]:\n* **Complex loop1** -              print(x)\n* **Subclasses** - class AI_Student(Student):\n* **Subclasses2** - fav_class = CIS 521\n* **List comprehension** -  squares = [x*x for x in nums]\n* **List comprehension with filter** -  li = [3, 6, 2, 7, 1, 9];[elem * 2 for elem in li if elem > 4] #[12, 14, 18]\n* **Dictionary comprehension** - dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}; # Check for items greater than 2 ;dict1_cond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0}\n* **Dictionary comprehension2** - dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6} # Identify odd and even entries#dict1_tripleCond = {k:('even' if v%2==0 else 'odd') for (k,v) in dict1.items()}\n* **Nested dictionary comp** - nested_dict = {'first':{'a':1}, 'second':{'b':2}}#float_dict = {outer_k: {float(inner_v) for (inner_k, inner_v) in outer_v.items()} for (outer_k, outer_v) in nested_dict.items()}#print(float_dict)\n* **List operations** - append(value), index(value), remove(value), count(value), a.reverse(), a.sort, b_list = sorted(a)\n* **Set operations** - add, remove, discard(silent), clear,pop,UNION x1.union(x2) or x1 | x2, INTERSECTION: x1.intersection(x2) or x1 & x2, MINUS x1.difference(x2) or x1 - x2\n* **Set operations2** - Update x1.update(x1,x2...),SYMMETRIC difference :x1.symmetric_difference(x2) or x1^x2\n* **Invalid set operations1** - a = [1, 2, 3]; {a} # TypeError: unhashable type: 'list'\n* **Invalid set operations2** - d = {'a': 1, 'b': 2}; {d} TypeError: unhashable type: 'dict'\n* **Dict operations** - dict.keys(), dict.values(), dict[key] = value\n* **Frozen_set** - frozen sets are immutable x = frozenset(['foo', 'bar', 'ba'])\n* **__init__** - (duck typing) The constructor for the class.\n* **__len__** - Define how len( obj ) works.\n* **__copy__** - Define how to copy a class.\n* **__cmp__** - Define how == works for class.\n* **__add__** - Define how + works for class\n* **__neg__** - Define how unary negation works for class\n* **__iter__** - Denotes if an oject is iterable iterable: __iter__ method which returns an iterator obj\n* **__next__** - Denotes if the object is a iterator : returns the next object in the sequence\n* **Generators** - Generators are iterators (with __next()__ method)\n* **Generators key benefits** - ▪ Less code than writing a standard iterator▪ Maintains local state automatically▪ Values are computed one at a time, as they’re needed▪ Avoids storing the entire sequence in memory▪ Good for aggregating (summing, counting) items. One pass.▪ Crucial for infinite sequences▪ Bad if you need to inspect the individual values\n* **Yield** - Functions that contain the yield keyword automatically return a generator when called\n* **Generators are better1** - Review: generators are good for aggregating items.\n* **Generators are better2** - sum(x for x in xrange(10**8) if x%5==0) #999999950000000L (Generator)\n* **Generators are better3** - Than version this which creates the whole list first\n* **Generators are better4** - sum([x for x in xrange(10**8) if x%5==0]) 999999950000000L (List comprehension)\n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "46MQLR-Gx8dZ"
      },
      "source": [
        "#Module 3 - Search Problems"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "BN0b1eVYx-nd"
      },
      "source": [
        "# Russell and Norvig, AIMA 4th Edition, AIMA Chapter 3 “Problem Solving by Search” (3.1-3.4)\n",
        "chapter3_1 = \"\"\" Goal Formulation, problem forumulation, Search, Execution \"\"\"\n",
        "chapter3_2 = \"\"\" Your summary here \"\"\"\n",
        "chapter3_3 = \"\"\" A search algorithm takes a search problem as input and returns a solution or an indication of failure \"\"\"\n",
        "chapter3_4 = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "\n",
        "# Lecture notes \n",
        "module3_lecture_notes = \"\"\" \n",
        "\n",
        "function AC-3(csp) return the CSP, possibly with reduced domains\n",
        "inputs: csp, a binary csp with variables {X1, X2, …, Xn}\n",
        "local variables: queue, a queue of arcs initially the arcs in csp\n",
        "while queue is not empty do\n",
        "(Xi,Xj) ¬ queue.pop()\n",
        "if REMOVE-INCONSISTENT-VALUES(Xi\n",
        ", Xj\n",
        ") then\n",
        "for each Xk in NEIGHBORS[Xi ] – {Xj} do\n",
        "add (Xk, Xi\n",
        ") to queue\n",
        "function REMOVE-INCONSISTENT-VALUES(Xi\n",
        ", Xj\n",
        ") return true iff we remove a value\n",
        "removed ¬ false\n",
        "for each x in DOMAIN[Xi\n",
        "] do\n",
        "if no value y in DOMAIN[Xj\n",
        "] allows (x,y) to satisfy the constraints between Xi and Xj\n",
        "then delete x from DOMAIN[Xi\n",
        "]; removed ¬ true\n",
        "return removed\n",
        "\n",
        "\"\"\"\n",
        "\n",
        "# Definitions of concepts from Module 3.\n",
        "# You can define any bolded term in the textbook, \n",
        "# but you most use your own words for the definitions.\n",
        "# Each definition should be at most 20 words long.\n",
        "module3_definitions = {\n",
        "\"problem-solving agent\" : \"an agent that can formulate a plan via search\", \n",
        "\"search\":\"Before taking any action in the real world. Thea gent simulates sequence of actions in its model,\\\n",
        "searching until it finds a sequence of actions that reaches the goal. Such a sequence is called a solution.\",\n",
        "\"execution\":\"The agent can now execute the actins in the solution, one at a time.\",\n",
        "\"state space\": \"A set of possible states the environment an be in.\",\n",
        "\"Inital States\": \" & Goal states.\",\n",
        "\"Transition Model\": \" A transition model describes what each action does Result(Arad,ToZerind) = Zerind.\",\n",
        "\"Action Cost Function\": \"Numerical cost of applying action a in the state s to reach state s prime.\",\n",
        "\"Open Loop\": \"In a open loop system, once the agent has found a solution - it will execute action blindly.\",\n",
        "\"Closed Loop\" : \"In a closed loop system, the agent monitors its perceots and engages actively with the decision making model to make changes due to non deterministic nature of its environment.\",\n",
        "\"Optimal Solution\":\"A solution with the lowest path cost among all solutions.\"\n",
        "}\n",
        "\n"
      ],
      "execution_count": 47,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "lvtN3p7ET3Tx",
        "cellView": "form",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 796
        },
        "outputId": "6c121ab0-8f73-4a0d-cee5-45dea1d9ddd7"
      },
      "source": [
        "#@title Preview of your summary\n",
        "#@markdown You don't need to modify anything in this cell. Just press play.\n",
        "module3_sections = [\n",
        "  (\"AIMA Chapter 3.1 Problem-Solving Agents\", chapter3_1),\n",
        "  (\"AIMA Chapter 3.2 Example Problems\", chapter3_2),\n",
        "  (\"AIMA Chapter 3.3 Search Algorithms\", chapter3_3),\n",
        "  (\"AIMA Chapter 3.4 Uninformed Search Strategies\", chapter3_4),\n",
        "  (\"Module 3 Lecture Notes\", module3_lecture_notes),\n",
        "]\n",
        "\n",
        "module3_markdown = format_module(3, \"Search Problems\", module3_sections, module3_definitions)\n",
        "display(Markdown(module3_markdown))"
      ],
      "execution_count": 48,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "## WARNING: Your definition of 'search' is too long!\nWARNING: Your definition of 'Closed Loop' is too long!\n# Module 3: Search Problems\n## AIMA Chapter 3.1 Problem-Solving Agents\n Goal Formulation, problem forumulation, Search, Execution \n\n## AIMA Chapter 3.2 Example Problems\n Your summary here \n\n## AIMA Chapter 3.3 Search Algorithms\n A search algorithm takes a search problem as input and returns a solution or an indication of failure \n\n## AIMA Chapter 3.4 Uninformed Search Strategies\n Your summary here \n\n## Module 3 Lecture Notes\n \n\nfunction AC-3(csp) return the CSP, possibly with reduced domains\ninputs: csp, a binary csp with variables {X1, X2, …, Xn}\nlocal variables: queue, a queue of arcs initially the arcs in csp\nwhile queue is not empty do\n(Xi,Xj) ¬ queue.pop()\nif REMOVE-INCONSISTENT-VALUES(Xi\n, Xj\n) then\nfor each Xk in NEIGHBORS[Xi ] – {Xj} do\nadd (Xk, Xi\n) to queue\nfunction REMOVE-INCONSISTENT-VALUES(Xi\n, Xj\n) return true iff we remove a value\nremoved ¬ false\nfor each x in DOMAIN[Xi\n] do\nif no value y in DOMAIN[Xj\n] allows (x,y) to satisfy the constraints between Xi and Xj\nthen delete x from DOMAIN[Xi\n]; removed ¬ true\nreturn removed\n\n\n\n## Module 3 Definitions\n* **Problem-solving agent** - an agent that can formulate a plan via search\n* **Search** - Before taking any action in the real world. Thea gent simulates sequence of actions in its model,searching until it finds a sequence of actions that reaches the goal. Such a sequence is called a solution.\n* **Execution** - The agent can now execute the actins in the solution, one at a time.\n* **State space** - A set of possible states the environment an be in.\n* **Inital states** -  & Goal states.\n* **Transition model** -  A transition model describes what each action does Result(Arad,ToZerind) = Zerind.\n* **Action cost function** - Numerical cost of applying action a in the state s to reach state s prime.\n* **Open loop** - In a open loop system, once the agent has found a solution - it will execute action blindly.\n* **Closed loop** - In a closed loop system, the agent monitors its perceots and engages actively with the decision making model to make changes due to non deterministic nature of its environment.\n* **Optimal solution** - A solution with the lowest path cost among all solutions.\n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "WHREsHSSyA-h"
      },
      "source": [
        "#Module 4 - Informed Search"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "tuFC8w7CyCns"
      },
      "source": [
        "# Russell and Norvig, AIMA Chapter 3 “Problem Solving by Search” (3.5-3.6)\n",
        "chapter3_5 = \"\"\" Your summary here \"\"\"\n",
        "chapter3_6 = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "\n",
        "# Lecture notes \n",
        "module4_lecture_notes = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "# Definitions\n",
        "module4_definitions = {\n",
        "\"concept\" : \"definition\",    \n",
        "}\n",
        "\n"
      ],
      "execution_count": 49,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "vqtEs0gHs9sO",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 341
        },
        "cellView": "form",
        "outputId": "5b83d6ec-ac1f-4f92-e47d-fa9b822e2efb"
      },
      "source": [
        "#@title Preview of your summary\n",
        "#@markdown You don't need to modify anything in this cell. Just press play.\n",
        "module4_sections = [\n",
        "  (\"AIMA Chapter 3.5 Informed (Heuristic) Search Strategies\", chapter3_5),\n",
        "  (\"AIMA Chapter 3.6 Heuristic Functions\", chapter3_6),\n",
        "  (\"Module 4 Lecture Notes\", module4_lecture_notes),\n",
        "]\n",
        "\n",
        "module4_markdown = format_module(4, \"Informed Search\", \n",
        "                         module4_sections, \n",
        "                         module4_definitions)\n",
        "display(Markdown(module4_markdown))"
      ],
      "execution_count": 50,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "# Module 4: Informed Search\n## AIMA Chapter 3.5 Informed (Heuristic) Search Strategies\n Your summary here \n\n## AIMA Chapter 3.6 Heuristic Functions\n Your summary here \n\n## Module 4 Lecture Notes\n Your summary here \n\n## Module 4 Definitions\n* **Concept** - definition\n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "F3FPMzG9yUls"
      },
      "source": [
        "#Module 5 - Adversarial Search and Games"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "iEIOpL7vyWcc"
      },
      "source": [
        "# Russell and Norvig, AIMA chapter 5 \"Adversarial Search\"”\" (5.1-5.3, and 5.5).\n",
        "chapter5_1 = \"\"\" Your summary here \"\"\"\n",
        "chapter5_2 = \"\"\" Your summary here \"\"\"\n",
        "chapter5_3 = \"\"\" Your summary here \"\"\"\n",
        "chapter5_5 = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "# Russell and Norvig, AIMA Chapter 16 “Making Simple Decisions” (16.1-16.3)\n",
        "chapter16_1 = \"\"\" Your summary here \"\"\"\n",
        "chapter16_2 = \"\"\" Your summary here \"\"\"\n",
        "chapter16_3 = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "# Lecture notes \n",
        "module5_lecture_notes = \"\"\"\n",
        "```\n",
        "############################################################\n",
        "# Section 1: Dominoes Game - A* Search\n",
        "############################################################\n",
        "\n",
        "def create_dominoes_game(rows, cols):\n",
        "    new_board = [[False for j in range(cols)] for i in range(rows)]\n",
        "    return DominoesGame(new_board)\n",
        "\n",
        "\n",
        "class DominoesGame(object):\n",
        "\n",
        "    # Required\n",
        "    def __init__(self, board):\n",
        "        self.board = board[:]\n",
        "        self.row_count = len(board)\n",
        "        if len(board[0]) != 0:\n",
        "            self.col_count = len(board[0])\n",
        "        else:\n",
        "            self.col_count = 0\n",
        "\n",
        "    def reset(self):\n",
        "        for i in range(self.row_count):\n",
        "            for j in range(self.col_count):\n",
        "                self.board[i][j] = False\n",
        "\n",
        "    def get_board(self):\n",
        "        return self.board\n",
        "\n",
        "    def is_legal_move(self, row, col, vertical):\n",
        "        if vertical:\n",
        "            if 0 <= row < self.row_count - 1 and col < self.col_count:\n",
        "                return not (self.board[row][col] or self.board[row + 1][col])\n",
        "            else:\n",
        "                return False\n",
        "        else:\n",
        "            if 0 <= col < self.col_count - 1 and row < self.row_count:\n",
        "                return not (self.board[row][col] or self.board[row][col + 1])\n",
        "            else:\n",
        "                return False\n",
        "\n",
        "    def legal_moves(self, vertical):\n",
        "        moves = []\n",
        "        for row in range(self.row_count):\n",
        "            for col in range(self.col_count):\n",
        "                if self.is_legal_move(row, col, vertical):\n",
        "                    yield (row, col)\n",
        "\n",
        "    def perform_move(self, row, col, vertical):\n",
        "        if self.is_legal_move(row, col, vertical):\n",
        "            self.board[row][col] = True\n",
        "            if vertical:\n",
        "                self.board[row + 1][col] = True\n",
        "            else:\n",
        "                self.board[row][col + 1] = True\n",
        "\n",
        "    def game_over(self, vertical):\n",
        "        moves = list(self.legal_moves(vertical))\n",
        "        return len(moves) == 0\n",
        "\n",
        "    def copy(self):\n",
        "        return DominoesGame([row[:] for row in self.board])\n",
        "\n",
        "    def successors(self, vertical):\n",
        "        for x, y in self.legal_moves(vertical):\n",
        "            new_board = self.copy()\n",
        "            new_board.perform_move(x, y, vertical)\n",
        "            yield (x, y), new_board\n",
        "\n",
        "    def get_random_move(self, vertical):\n",
        "        return random.choice(list(self.legal_moves(vertical)))\n",
        "\n",
        "    def ab_max_value(self, alpha, beta, current_loc, limit, vertical):\n",
        "        left = list(self.successors(vertical))\n",
        "        right = list(self.successors(not vertical))\n",
        "\n",
        "        if limit == 0 or self.game_over(vertical):\n",
        "            return current_loc, len(left) - len(right), 1\n",
        "\n",
        "        # start with negative infinity to find the max value.\n",
        "        max_value = -math.inf\n",
        "\n",
        "        state = 0\n",
        "        curr_move = current_loc\n",
        "        for loc, next_board in left:\n",
        "            # switch between vertical and horizontal plays at each recursive call\n",
        "            move, temp, count = next_board.ab_min_value(alpha=alpha, beta=beta, current_loc=loc, \\\n",
        "                                                   limit=limit - 1, vertical=not vertical)\n",
        "            state = state + count\n",
        "\n",
        "            if temp > max_value:\n",
        "                curr_move = loc\n",
        "                max_value = temp\n",
        "            if max_value >= beta:\n",
        "                return curr_move, max_value, state\n",
        "\n",
        "            alpha = max(alpha, max_value)\n",
        "\n",
        "        return curr_move, max_value, state\n",
        "\n",
        "    def ab_min_value(self, alpha, beta, current_loc, limit, vertical):\n",
        "        right = list(self.successors(not vertical))\n",
        "        left = list(self.successors(vertical))\n",
        "\n",
        "        if limit == 0 or self.game_over(vertical):\n",
        "            return current_loc, len(right) - len(left), 1\n",
        "\n",
        "        state = 0\n",
        "        # start with postitive infinity to find the min value\n",
        "        min_value = math.inf\n",
        "        curr_move = current_loc\n",
        "\n",
        "        for loc, next_board in left:\n",
        "            # switch between vertical and horizontal plays at each recursive call\n",
        "            move, temp, count = next_board.ab_max_value(alpha=alpha, beta=beta, current_loc=loc, \\\n",
        "                                                   limit=limit - 1, vertical=not vertical)\n",
        "            state = state + count\n",
        "\n",
        "            if temp < min_value:\n",
        "                curr_move = loc\n",
        "                min_value = temp\n",
        "            if min_value <= alpha:\n",
        "                return curr_move, min_value, state\n",
        "\n",
        "            beta = min(beta, min_value)\n",
        "\n",
        "        return curr_move, min_value, state\n",
        "\n",
        "    def get_best_move(self, vertical, limit):\n",
        "        return self.ab_max_value(alpha=-math.inf, beta=math.inf, moves=None, limit=limit, vertical=vertical)\n",
        "\n",
        "    # Required\n",
        "    def get_best_move(self, vertical, limit):\n",
        "        return self.ab_max_value(alpha=-math.inf, beta=math.inf, \\\n",
        "                                 current_loc=None, limit=limit, vertical=vertical)\n",
        "```\n",
        " \"\"\"       \n",
        "\n",
        "# Definitions\n",
        "module5_definitions = {\n",
        "\"concept\" : \"definition\",    \n",
        "}\n",
        "\n",
        "\n"
      ],
      "execution_count": 51,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "id": "KUc_LgzDvWf-",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "outputId": "37203b1a-cf9c-47be-ccfe-4346713902ba"
      },
      "source": [
        "#@title Preview of your summary\n",
        "#@markdown You don't need to modify anything in this cell. Just press play.\n",
        "module5_sections = [\n",
        "  (\"AIMA Chapter 5.1 Game Theory\", chapter5_1),\n",
        "  (\"AIMA Chapter 5.2 Optimal Decisions in Games\", chapter5_2),\n",
        "  (\"AIMA Chapter 5.3 Heuristic Alpha–Beta Tree Search\", chapter5_3),\n",
        "  (\"AIMA Chapter 5.5 Stochastic Games\", chapter5_5),\n",
        "  (\"AIMA Chapter 16.1 Combining Beliefs and Desires under Uncertainty\", chapter16_1),\n",
        "  (\"AIMA Chapter 16.2 The Basis of Utility Theory\", chapter16_2),\n",
        "  (\"AIMA Chapter 16.3 Utility Functions\", chapter16_3),\n",
        "  (\"Module 5 Lecture Notes\", module5_lecture_notes),\n",
        "]\n",
        "\n",
        "module5_markdown = format_module(5, \"Adversarial Search and Games\", \n",
        "                         module5_sections, \n",
        "                         module5_definitions)\n",
        "display(Markdown(module5_markdown))"
      ],
      "execution_count": 52,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "# Module 5: Adversarial Search and Games\n## AIMA Chapter 5.1 Game Theory\n Your summary here \n\n## AIMA Chapter 5.2 Optimal Decisions in Games\n Your summary here \n\n## AIMA Chapter 5.3 Heuristic Alpha–Beta Tree Search\n Your summary here \n\n## AIMA Chapter 5.5 Stochastic Games\n Your summary here \n\n## AIMA Chapter 16.1 Combining Beliefs and Desires under Uncertainty\n Your summary here \n\n## AIMA Chapter 16.2 The Basis of Utility Theory\n Your summary here \n\n## AIMA Chapter 16.3 Utility Functions\n Your summary here \n\n## Module 5 Lecture Notes\n\n```\n############################################################\n# Section 1: Dominoes Game - A* Search\n############################################################\n\ndef create_dominoes_game(rows, cols):\n    new_board = [[False for j in range(cols)] for i in range(rows)]\n    return DominoesGame(new_board)\n\n\nclass DominoesGame(object):\n\n    # Required\n    def __init__(self, board):\n        self.board = board[:]\n        self.row_count = len(board)\n        if len(board[0]) != 0:\n            self.col_count = len(board[0])\n        else:\n            self.col_count = 0\n\n    def reset(self):\n        for i in range(self.row_count):\n            for j in range(self.col_count):\n                self.board[i][j] = False\n\n    def get_board(self):\n        return self.board\n\n    def is_legal_move(self, row, col, vertical):\n        if vertical:\n            if 0 <= row < self.row_count - 1 and col < self.col_count:\n                return not (self.board[row][col] or self.board[row + 1][col])\n            else:\n                return False\n        else:\n            if 0 <= col < self.col_count - 1 and row < self.row_count:\n                return not (self.board[row][col] or self.board[row][col + 1])\n            else:\n                return False\n\n    def legal_moves(self, vertical):\n        moves = []\n        for row in range(self.row_count):\n            for col in range(self.col_count):\n                if self.is_legal_move(row, col, vertical):\n                    yield (row, col)\n\n    def perform_move(self, row, col, vertical):\n        if self.is_legal_move(row, col, vertical):\n            self.board[row][col] = True\n            if vertical:\n                self.board[row + 1][col] = True\n            else:\n                self.board[row][col + 1] = True\n\n    def game_over(self, vertical):\n        moves = list(self.legal_moves(vertical))\n        return len(moves) == 0\n\n    def copy(self):\n        return DominoesGame([row[:] for row in self.board])\n\n    def successors(self, vertical):\n        for x, y in self.legal_moves(vertical):\n            new_board = self.copy()\n            new_board.perform_move(x, y, vertical)\n            yield (x, y), new_board\n\n    def get_random_move(self, vertical):\n        return random.choice(list(self.legal_moves(vertical)))\n\n    def ab_max_value(self, alpha, beta, current_loc, limit, vertical):\n        left = list(self.successors(vertical))\n        right = list(self.successors(not vertical))\n\n        if limit == 0 or self.game_over(vertical):\n            return current_loc, len(left) - len(right), 1\n\n        # start with negative infinity to find the max value.\n        max_value = -math.inf\n\n        state = 0\n        curr_move = current_loc\n        for loc, next_board in left:\n            # switch between vertical and horizontal plays at each recursive call\n            move, temp, count = next_board.ab_min_value(alpha=alpha, beta=beta, current_loc=loc,                                                    limit=limit - 1, vertical=not vertical)\n            state = state + count\n\n            if temp > max_value:\n                curr_move = loc\n                max_value = temp\n            if max_value >= beta:\n                return curr_move, max_value, state\n\n            alpha = max(alpha, max_value)\n\n        return curr_move, max_value, state\n\n    def ab_min_value(self, alpha, beta, current_loc, limit, vertical):\n        right = list(self.successors(not vertical))\n        left = list(self.successors(vertical))\n\n        if limit == 0 or self.game_over(vertical):\n            return current_loc, len(right) - len(left), 1\n\n        state = 0\n        # start with postitive infinity to find the min value\n        min_value = math.inf\n        curr_move = current_loc\n\n        for loc, next_board in left:\n            # switch between vertical and horizontal plays at each recursive call\n            move, temp, count = next_board.ab_max_value(alpha=alpha, beta=beta, current_loc=loc,                                                    limit=limit - 1, vertical=not vertical)\n            state = state + count\n\n            if temp < min_value:\n                curr_move = loc\n                min_value = temp\n            if min_value <= alpha:\n                return curr_move, min_value, state\n\n            beta = min(beta, min_value)\n\n        return curr_move, min_value, state\n\n    def get_best_move(self, vertical, limit):\n        return self.ab_max_value(alpha=-math.inf, beta=math.inf, moves=None, limit=limit, vertical=vertical)\n\n    # Required\n    def get_best_move(self, vertical, limit):\n        return self.ab_max_value(alpha=-math.inf, beta=math.inf,                                  current_loc=None, limit=limit, vertical=vertical)\n```\n \n\n## Module 5 Definitions\n* **Concept** - definition\n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qVSQhTVSyYYO"
      },
      "source": [
        "#Module 6 - \"Constraint Satisfaction Problems\""
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Wm4mfW82yaRH"
      },
      "source": [
        "# Russell and Norvig, AIMA, Chapter 6 “Constraint Satisfaction Problems” (6.1-6.5)\n",
        "chapter6_1 = \"\"\" A problem is solved when each variable has a value that \"\"\"\n",
        "chapter6_2 = \"\"\" Your summary here \"\"\"\n",
        "chapter6_3 = \"\"\" Your summary here \"\"\"\n",
        "chapter6_4 = \"\"\" Your summary here \"\"\"\n",
        "chapter6_5 = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "\n",
        "# Lecture notes \n",
        "module6_lecture_notes = \"\"\"A CSP consists of:\n",
        "1. X : Finite set of variables X1, X2, ..., Xn\n",
        "2. D : Nonempty domain of possible values for each variable\n",
        "D1, D2, ... Dn where Di = {v1, ..., vk}\n",
        "3. C: Finite set of constraints C1, C2, ..., Cm\n",
        "4. Each constraint Ci limits the values that variables can take, e.g., X1 ≠ X2 A state is defined as an assignment of values to some or all variables.\n",
        "5. A consistent assignment does not violate the constraints. \n",
        " Example problem: Sudoku\n",
        " An assignment is complete when every variable is assigned a value.\n",
        " A solution to a CSP is a complete, consistent assignment.\n",
        " Solutions to CSPs can be found by a completely general purpose algorithm, given only the formal specification of the CSP.\n",
        " Beyond our scope: CSPs that require a solution that maximizes an objective function.\n",
        "\n",
        " Assumptions about the world: a single agent, deterministic actions, fully observed state, discrete state space\n",
        " Planning: sequences of actions\n",
        "▪ The path to the goal is the important thing\n",
        "▪ Paths have various costs, depths\n",
        "▪ Heuristics give problem-specific guidance\n",
        "Identification: assignments to variables\n",
        "▪ The goal itself is important, not the path\n",
        "▪ All paths at the same depth (for some formulations)\n",
        "▪ CSPs are specialized for identification problems\n",
        " \n",
        "When to Iterate, When to Stop?\n",
        "The crucial principle:\n",
        "If a value is removed from a node Xi,\n",
        "then the values on all of Xi’s neighbors must be reexamined.\n",
        "\n",
        "Why? Removing a value from a node may result in one of the neighbors becoming arc inconsistent, so we need to check...\n",
        "(but each neighbor Xj can only become inconsistent with respect to the removed values on Xi)\n",
        "\n",
        "A CSP can easily be expressed as a search problem\n",
        "▪ Initial State: the empty assignment {}.\n",
        "▪ Successor function: Assign value to any unassigned variable provided that there is not a constraint conflict.\n",
        "▪ Goal test: the current assignment is complete.\n",
        "▪ Path cost: a constant cost for every step.\n",
        "o Solution is always found at depth n, for n variables\n",
        "▪ Hence Depth First Search can be used\n",
        "\n",
        "\n",
        "Question 8\n",
        "In arc consistency checks we have a pair of nodes, X (in the tail of the arc) and Y (at the head of the arc).  \n",
        "In order to make X arc-consistent with Y, which of the following is true (assuming only inequality constraints):\n",
        "\n",
        "1. We must check all values in X\n",
        "2. Remove values from X that are present in Y\n",
        "3. In arc consistency checks, we check all values in the tail and remove any that are not allowed by the head.  We always check all values on the tail, \n",
        "but we can stop checking values on the head when we first see a value that allows the value that we’re checking on the tail.\n",
        "\n",
        "AC-3 can solve all solvable sudoku problems in O(n^n +1 ) time.\n",
        "\n",
        "```\n",
        "# Include your imports here, if any are used.\n",
        "import copy\n",
        "import queue\n",
        "import collections\n",
        "\n",
        "\n",
        "############################################################\n",
        "# Sudoku Solver\n",
        "############################################################\n",
        "\n",
        "def sudoku_cells():\n",
        "    return [(i, j) for i in range(9) for j in range(9)]\n",
        "\n",
        "\n",
        "def get_grid_hash(r1, c1):\n",
        "    row_index = (int)(r1 / 3)\n",
        "    col_index = (int)(c1 / 3)\n",
        "\n",
        "    # grid_hash - to map cells to unique grids\n",
        "    return 3 * row_index + col_index\n",
        "\n",
        "\n",
        "def sudoku_grids():\n",
        "    grids = {}\n",
        "    for r1 in range(9):\n",
        "        for c1 in range(9):\n",
        "            grids[(r1, c1)] = get_grid_hash(r1, c1)\n",
        "    return grids\n",
        "\n",
        "\n",
        "def sudoku_arcs():\n",
        "    grids = sudoku_grids()\n",
        "    arcs = set()\n",
        "    for r1, c1 in sudoku_cells():\n",
        "        for r2, c2 in sudoku_cells():\n",
        "            # verify if the cells are in the same constraint group - same row or same column or grid\n",
        "            if (r1 == r2 or c1 == c2 or grids[(r1, c1)] == grids[(r2, c2)]) \\\n",
        "                    and (r1, c1) != (r2, c2):  # should not be the same cell\n",
        "                arcs.add(((r1, c1), (r2, c2)))\n",
        "    return arcs\n",
        "\n",
        "\n",
        "def read_board(path):\n",
        "    with open(path) as file:\n",
        "        board = file.readlines()  # read all the lines at once.\n",
        "    board_dict = dict()\n",
        "    for r1 in range(9):\n",
        "        for c1 in range(9):\n",
        "            if board[r1][c1] == '*':\n",
        "                board_dict[(r1, c1)] = set(list(range(1, 10)))  # * indicates (1-9) are open to be selected\n",
        "            else:\n",
        "                board_dict[(r1, c1)] = {int(board[r1][c1])}  # cell value is given and fixed.\n",
        "    return board_dict\n",
        "\n",
        "\n",
        "class Sudoku(object):\n",
        "    CELLS = sudoku_cells()\n",
        "    ARCS = sudoku_arcs()\n",
        "    GRIDS = sudoku_grids()\n",
        "\n",
        "    def __init__(self, board):\n",
        "        self.board = board\n",
        "\n",
        "    def get_values(self, cell):\n",
        "        return self.board[cell]\n",
        "\n",
        "    def get_arc_neighbours(self, cell1):\n",
        "        return set([arc for arc in Sudoku.ARCS if arc[0] == cell1 or arc[1] == cell1])\n",
        "\n",
        "    def get_cell_neighbours(self, cell1):\n",
        "        arc_neighbors = set([arc for arc in Sudoku.ARCS if arc[0] == cell1 or arc[1] == cell1])\n",
        "        cell_neighbors = set()\n",
        "        for arc in arc_neighbors:\n",
        "            if arc[0] == cell1:\n",
        "                cell_neighbors.add(arc[1])\n",
        "            elif arc[1] == cell1:\n",
        "                cell_neighbors.add(arc[0])\n",
        "        return cell_neighbors\n",
        "\n",
        "    def is_solved(self):\n",
        "        for cell1, cell2 in self.ARCS:\n",
        "            if len(self.board[cell1]) != 1 or len(self.board[cell2]) != 1 or self.board[cell1] == self.board[cell2]:\n",
        "                return False\n",
        "        return True\n",
        "\n",
        "    def remove_inconsistent_values(self, cell1, cell2):\n",
        "        removed = False\n",
        "        if cell1 == cell2:\n",
        "            return removed\n",
        "\n",
        "        inconsistent_values = set()\n",
        "\n",
        "        for x in self.board[cell1]:\n",
        "            # if x is inconsistent with all values in cell2, add x to the inconsistent_values\n",
        "            cell2_values = self.get_values(cell2)\n",
        "            if x in cell2_values and len(cell2_values) == 1:\n",
        "                inconsistent_values.add(x)\n",
        "\n",
        "        # remove inconsistent values from cell 1\n",
        "        data = self.get_values(cell1)\n",
        "        for y in inconsistent_values:\n",
        "            data.remove(y)\n",
        "\n",
        "        return len(inconsistent_values) > 0\n",
        "\n",
        "    def infer_ac3(self):\n",
        "        queue = collections.deque(Sudoku.ARCS)\n",
        "        while queue:\n",
        "            (cell1, cell2) = queue.popleft()\n",
        "            if self.remove_inconsistent_values(cell1, cell2):\n",
        "                neighbours = self.get_arc_neighbours(cell1)\n",
        "                for cell in neighbours:\n",
        "                    queue.append(cell)\n",
        "        return self.is_solved()\n",
        "\n",
        "    def get_constrained_values(self, cell1):\n",
        "        neighbors = self.get_cell_neighbours(cell1)\n",
        "        constrained_vals = []\n",
        "        for cell in neighbors:\n",
        "            for value in self.board.get(cell):\n",
        "                constrained_vals.append(value)\n",
        "        return constrained_vals\n",
        "\n",
        "    def get_guesses(self):\n",
        "        for cell in self.CELLS:\n",
        "            if len(self.board[cell]) > 1:\n",
        "                for x in self.board[cell]:\n",
        "                    option = copy.deepcopy(self.board)\n",
        "                    option[cell] = {x}\n",
        "                    yield option\n",
        "                break\n",
        "\n",
        "    def infer_improved(self):\n",
        "        additional_inference_made = True\n",
        "        while additional_inference_made:\n",
        "            self.infer_ac3()\n",
        "\n",
        "            additional_inference_made = False\n",
        "\n",
        "            for cell in Sudoku.CELLS:\n",
        "                options = self.board[cell]\n",
        "                if len(options) > 1:\n",
        "                    if self.revise_constrained_cells(cell):\n",
        "                        additional_inference_made = True\n",
        "                        continue\n",
        "        return self.is_solved()\n",
        "\n",
        "    def infer_with_guessing(self):\n",
        "        boards = queue.deque([copy.deepcopy(self.board)])\n",
        "        while len(boards) > 0:\n",
        "            guess = boards.popleft()\n",
        "            self.board = guess\n",
        "            if self.infer_improved():\n",
        "                break\n",
        "            else:\n",
        "                boards += list(self.get_guesses())\n",
        "\n",
        "    def check_consistency(self, cell, neighbors):\n",
        "        for x in self.board[cell]:\n",
        "            # assume this value is correct\n",
        "            consistent = True\n",
        "            for cell in neighbors:\n",
        "                if x in self.board[cell]:\n",
        "                    consistent = False\n",
        "                    break\n",
        "\n",
        "            # return the unique value x\n",
        "            if consistent:\n",
        "                return True, set([x])\n",
        "        return False, None\n",
        "\n",
        "    def revise_constrained_cells(self, cell1):\n",
        "        revised = False\n",
        "        if len(self.board[cell1]) > 1:\n",
        "            same_row = set()\n",
        "            same_col = set()\n",
        "            same_grid = set()\n",
        "\n",
        "            for cell2 in self.CELLS:\n",
        "                if cell1 != cell2:\n",
        "                    if self.GRIDS[cell1] == self.GRIDS[cell2]:  # same 3 X 3 grid\n",
        "                        same_grid.add(cell2)\n",
        "                    if cell1[0] == cell2[0]:  # same row\n",
        "                        same_row.add(cell2)\n",
        "                    if cell1[1] == cell2[1]:  # same col\n",
        "                        same_col.add(cell2)\n",
        "\n",
        "            # process each constriant group one at a time - to ease into an optimal solution iteratively\n",
        "\n",
        "            # process grid constraint arcs as the first preference\n",
        "            replace_cell, cell = self.check_consistency(cell1, same_grid)\n",
        "            if replace_cell:\n",
        "                self.board[cell1] = cell\n",
        "                return True\n",
        "\n",
        "            replace_cell, cell = self.check_consistency(cell1, same_row)\n",
        "\n",
        "            # process row constraint arcs as the second preference\n",
        "            if replace_cell:\n",
        "                self.board[cell1] = cell\n",
        "                return True\n",
        "\n",
        "            # process col constraint arcs as the third preference\n",
        "            replace_cell, cell = self.check_consistency(cell1, same_col)\n",
        "            if replace_cell:\n",
        "                self.board[cell1] = cell\n",
        "                return True\n",
        "\n",
        "        return revised\n",
        "\n",
        "```\n",
        "\n",
        "  \"\"\"\n",
        "\n",
        "# Definitions\n",
        "module6_definitions = {\n",
        "\"Inference\" : \"Using problem constraints to remove the number of legal values on variables. CSPs allow for a mixture of search (expanding a node to visit its successors) and inference, where the current choice of nodes and values are reduced by propagating constraints.\" ,\n",
        "\"Declarative programming\":\"Representing a problem formally (without regards to implementation) as a constraint satisfaction problem is an instance of Declarative prog\",\n",
        "\"chapter6_5\":\"\"\n",
        "}\n",
        "\n",
        "\n"
      ],
      "execution_count": 53,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "id": "kKWbCsbfxL3R",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "outputId": "3cc3b0f9-1785-4db1-8260-f3c1b2b6c65a"
      },
      "source": [
        "#@title Preview of your summary\n",
        "#@markdown You don't need to modify anything in this cell. Just press play.\n",
        "module6_sections = [\n",
        "  (\"AIMA Chapter 6.1 Defining Constraint Satisfaction Problems\", chapter6_1),\n",
        "  (\"AIMA Chapter 6.2 Constraint Propagation: Inference in CSPs\", chapter6_2),\n",
        "  (\"AIMA Chapter 6.3 Backtracking Search for CSPs\", chapter6_3),\n",
        "  (\"AIMA Chapter 6.4 Local Search for CSPs\", chapter6_4),\n",
        "  (\"AIMA Chapter 6.5 The Structure of Problems\", chapter6_5),\n",
        "  (\"Module 6 Lecture Notes\", module6_lecture_notes),\n",
        "]\n",
        "\n",
        "module6_markdown = format_module(6, \"Constraint Satisfaction Problems\", \n",
        "                         module6_sections, \n",
        "                         module6_definitions)\n",
        "display(Markdown(module6_markdown))"
      ],
      "execution_count": 54,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "## WARNING: Your summary of 'Module 6 Lecture Notes' is too long!\n## WARNING: Your definition of 'Inference' is too long!\n# Module 6: Constraint Satisfaction Problems\n## AIMA Chapter 6.1 Defining Constraint Satisfaction Problems\n A problem is solved when each variable has a value that \n\n## AIMA Chapter 6.2 Constraint Propagation: Inference in CSPs\n Your summary here \n\n## AIMA Chapter 6.3 Backtracking Search for CSPs\n Your summary here \n\n## AIMA Chapter 6.4 Local Search for CSPs\n Your summary here \n\n## AIMA Chapter 6.5 The Structure of Problems\n Your summary here \n\n## Module 6 Lecture Notes\nA CSP consists of:\n1. X : Finite set of variables X1, X2, ..., Xn\n2. D : Nonempty domain of possible values for each variable\nD1, D2, ... Dn where Di = {v1, ..., vk}\n3. C: Finite set of constraints C1, C2, ..., Cm\n4. Each constraint Ci limits the values that variables can take, e.g., X1 ≠ X2 A state is defined as an assignment of values to some or all variables.\n5. A consistent assignment does not violate the constraints. \n Example problem: Sudoku\n An assignment is complete when every variable is assigned a value.\n A solution to a CSP is a complete, consistent assignment.\n Solutions to CSPs can be found by a completely general purpose algorithm, given only the formal specification of the CSP.\n Beyond our scope: CSPs that require a solution that maximizes an objective function.\n\n Assumptions about the world: a single agent, deterministic actions, fully observed state, discrete state space\n Planning: sequences of actions\n▪ The path to the goal is the important thing\n▪ Paths have various costs, depths\n▪ Heuristics give problem-specific guidance\nIdentification: assignments to variables\n▪ The goal itself is important, not the path\n▪ All paths at the same depth (for some formulations)\n▪ CSPs are specialized for identification problems\n \nWhen to Iterate, When to Stop?\nThe crucial principle:\nIf a value is removed from a node Xi,\nthen the values on all of Xi’s neighbors must be reexamined.\n\nWhy? Removing a value from a node may result in one of the neighbors becoming arc inconsistent, so we need to check...\n(but each neighbor Xj can only become inconsistent with respect to the removed values on Xi)\n\nA CSP can easily be expressed as a search problem\n▪ Initial State: the empty assignment {}.\n▪ Successor function: Assign value to any unassigned variable provided that there is not a constraint conflict.\n▪ Goal test: the current assignment is complete.\n▪ Path cost: a constant cost for every step.\no Solution is always found at depth n, for n variables\n▪ Hence Depth First Search can be used\n\n\nQuestion 8\nIn arc consistency checks we have a pair of nodes, X (in the tail of the arc) and Y (at the head of the arc).  \nIn order to make X arc-consistent with Y, which of the following is true (assuming only inequality constraints):\n\n1. We must check all values in X\n2. Remove values from X that are present in Y\n3. In arc consistency checks, we check all values in the tail and remove any that are not allowed by the head.  We always check all values on the tail, \nbut we can stop checking values on the head when we first see a value that allows the value that we’re checking on the tail.\n\nAC-3 can solve all solvable sudoku problems in O(n^n +1 ) time.\n\n```\n# Include your imports here, if any are used.\nimport copy\nimport queue\nimport collections\n\n\n############################################################\n# Sudoku Solver\n############################################################\n\ndef sudoku_cells():\n    return [(i, j) for i in range(9) for j in range(9)]\n\n\ndef get_grid_hash(r1, c1):\n    row_index = (int)(r1 / 3)\n    col_index = (int)(c1 / 3)\n\n    # grid_hash - to map cells to unique grids\n    return 3 * row_index + col_index\n\n\ndef sudoku_grids():\n    grids = {}\n    for r1 in range(9):\n        for c1 in range(9):\n            grids[(r1, c1)] = get_grid_hash(r1, c1)\n    return grids\n\n\ndef sudoku_arcs():\n    grids = sudoku_grids()\n    arcs = set()\n    for r1, c1 in sudoku_cells():\n        for r2, c2 in sudoku_cells():\n            # verify if the cells are in the same constraint group - same row or same column or grid\n            if (r1 == r2 or c1 == c2 or grids[(r1, c1)] == grids[(r2, c2)])                     and (r1, c1) != (r2, c2):  # should not be the same cell\n                arcs.add(((r1, c1), (r2, c2)))\n    return arcs\n\n\ndef read_board(path):\n    with open(path) as file:\n        board = file.readlines()  # read all the lines at once.\n    board_dict = dict()\n    for r1 in range(9):\n        for c1 in range(9):\n            if board[r1][c1] == '*':\n                board_dict[(r1, c1)] = set(list(range(1, 10)))  # * indicates (1-9) are open to be selected\n            else:\n                board_dict[(r1, c1)] = {int(board[r1][c1])}  # cell value is given and fixed.\n    return board_dict\n\n\nclass Sudoku(object):\n    CELLS = sudoku_cells()\n    ARCS = sudoku_arcs()\n    GRIDS = sudoku_grids()\n\n    def __init__(self, board):\n        self.board = board\n\n    def get_values(self, cell):\n        return self.board[cell]\n\n    def get_arc_neighbours(self, cell1):\n        return set([arc for arc in Sudoku.ARCS if arc[0] == cell1 or arc[1] == cell1])\n\n    def get_cell_neighbours(self, cell1):\n        arc_neighbors = set([arc for arc in Sudoku.ARCS if arc[0] == cell1 or arc[1] == cell1])\n        cell_neighbors = set()\n        for arc in arc_neighbors:\n            if arc[0] == cell1:\n                cell_neighbors.add(arc[1])\n            elif arc[1] == cell1:\n                cell_neighbors.add(arc[0])\n        return cell_neighbors\n\n    def is_solved(self):\n        for cell1, cell2 in self.ARCS:\n            if len(self.board[cell1]) != 1 or len(self.board[cell2]) != 1 or self.board[cell1] == self.board[cell2]:\n                return False\n        return True\n\n    def remove_inconsistent_values(self, cell1, cell2):\n        removed = False\n        if cell1 == cell2:\n            return removed\n\n        inconsistent_values = set()\n\n        for x in self.board[cell1]:\n            # if x is inconsistent with all values in cell2, add x to the inconsistent_values\n            cell2_values = self.get_values(cell2)\n            if x in cell2_values and len(cell2_values) == 1:\n                inconsistent_values.add(x)\n\n        # remove inconsistent values from cell 1\n        data = self.get_values(cell1)\n        for y in inconsistent_values:\n            data.remove(y)\n\n        return len(inconsistent_values) > 0\n\n    def infer_ac3(self):\n        queue = collections.deque(Sudoku.ARCS)\n        while queue:\n            (cell1, cell2) = queue.popleft()\n            if self.remove_inconsistent_values(cell1, cell2):\n                neighbours = self.get_arc_neighbours(cell1)\n                for cell in neighbours:\n                    queue.append(cell)\n        return self.is_solved()\n\n    def get_constrained_values(self, cell1):\n        neighbors = self.get_cell_neighbours(cell1)\n        constrained_vals = []\n        for cell in neighbors:\n            for value in self.board.get(cell):\n                constrained_vals.append(value)\n        return constrained_vals\n\n    def get_guesses(self):\n        for cell in self.CELLS:\n            if len(self.board[cell]) > 1:\n                for x in self.board[cell]:\n                    option = copy.deepcopy(self.board)\n                    option[cell] = {x}\n                    yield option\n                break\n\n    def infer_improved(self):\n        additional_inference_made = True\n        while additional_inference_made:\n            self.infer_ac3()\n\n            additional_inference_made = False\n\n            for cell in Sudoku.CELLS:\n                options = self.board[cell]\n                if len(options) > 1:\n                    if self.revise_constrained_cells(cell):\n                        additional_inference_made = True\n                        continue\n        return self.is_solved()\n\n    def infer_with_guessing(self):\n        boards = queue.deque([copy.deepcopy(self.board)])\n        while len(boards) > 0:\n            guess = boards.popleft()\n            self.board = guess\n            if self.infer_improved():\n                break\n            else:\n                boards += list(self.get_guesses())\n\n    def check_consistency(self, cell, neighbors):\n        for x in self.board[cell]:\n            # assume this value is correct\n            consistent = True\n            for cell in neighbors:\n                if x in self.board[cell]:\n                    consistent = False\n                    break\n\n            # return the unique value x\n            if consistent:\n                return True, set([x])\n        return False, None\n\n    def revise_constrained_cells(self, cell1):\n        revised = False\n        if len(self.board[cell1]) > 1:\n            same_row = set()\n            same_col = set()\n            same_grid = set()\n\n            for cell2 in self.CELLS:\n                if cell1 != cell2:\n                    if self.GRIDS[cell1] == self.GRIDS[cell2]:  # same 3 X 3 grid\n                        same_grid.add(cell2)\n                    if cell1[0] == cell2[0]:  # same row\n                        same_row.add(cell2)\n                    if cell1[1] == cell2[1]:  # same col\n                        same_col.add(cell2)\n\n            # process each constriant group one at a time - to ease into an optimal solution iteratively\n\n            # process grid constraint arcs as the first preference\n            replace_cell, cell = self.check_consistency(cell1, same_grid)\n            if replace_cell:\n                self.board[cell1] = cell\n                return True\n\n            replace_cell, cell = self.check_consistency(cell1, same_row)\n\n            # process row constraint arcs as the second preference\n            if replace_cell:\n                self.board[cell1] = cell\n                return True\n\n            # process col constraint arcs as the third preference\n            replace_cell, cell = self.check_consistency(cell1, same_col)\n            if replace_cell:\n                self.board[cell1] = cell\n                return True\n\n        return revised\n\n```\n\n  \n\n## Module 6 Definitions\n* **Inference** - Using problem constraints to remove the number of legal values on variables. CSPs allow for a mixture of search (expanding a node to visit its successors) and inference, where the current choice of nodes and values are reduced by propagating constraints.\n* **Declarative programming** - Representing a problem formally (without regards to implementation) as a constraint satisfaction problem is an instance of Declarative prog\n* **Chapter6_5** - \n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RYESi2kMybL8"
      },
      "source": [
        "#Module 7 - Logical Agents"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "qIRJ4YYCzNHL"
      },
      "source": [
        "# Russell and Norvig, AIMA Chapter 7 “Logical Agents” (Sections 7.1-7.5)\n",
        "\n",
        "chapter7_1 = \"\"\" Your summary here \"\"\"\n",
        "chapter7_2 = \"\"\" Your summary here \"\"\"\n",
        "chapter7_3 = \"\"\" Your summary here \"\"\"\n",
        "chapter7_4 = \"\"\" Your summary here \"\"\"\n",
        "chapter7_5 = \"\"\" Your summary here \"\"\"\n",
        "\n",
        "# Lecture notes \n",
        "module7_lecture_notes = \"\"\"  PEAS: Specifying an Amazon delivery drone Performance measure:\n",
        "maximize profits - minimize time - obey laws governing airspace restrictions - deliver package to right location - keep package in good condition - avoid accidents - reduce noise - preserve battery life\n",
        "Environment:      \\\n",
        "- airspace - obstacles when airborne (other drones, birds, buildings, trees, utility poles) - obstacles when landing (pets, patio furniture, lawnmowers, people, cars) - weather - distances/route information between warehouse and destinations - position of houses, and spaces that are safe for drop-off- package weight\n",
        "  \n",
        " \\\n",
        "  PEAS: Specifying an Amazon delivery drone Actuators:  \\\n",
        "- Propellers and flight control system- Payload actuators: E.g. Arm/basket/claw for picking up, dropping off packages- Lights or signals - Mechanism to announce/verify delivery- Device for delivering packages to customers\n",
        "Sensors:  \\\n",
        "\\\n",
        " GPS - radar/Lidar- altitude sensor- weather sensors (barometer, etc). \\\n",
        "- gyroscope- accelerometer.  \\\n",
        "- camera   \\\n",
        "- rotor sensors.  \\\n",
        "- weight sensor to recognize package. \\\n",
        "  \"\"\"\n",
        "\n",
        "# Definitions\n",
        "module7_definitions = {\n",
        "\"concept\" : \"definition\",    \n",
        "}\n",
        "\n"
      ],
      "execution_count": 55,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "id": "lXNcHuKjyNXa",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 649
        },
        "outputId": "0ec4ec56-7cdc-4d03-dbd8-3de472f74e3c"
      },
      "source": [
        "#@title Preview of your summary\n",
        "#@markdown You don't need to modify anything in this cell. Just press play.\n",
        "module7_sections = [\n",
        "  (\"AIMA Chapter 7.1 Knowledge-Based Agents\", chapter7_1),\n",
        "  (\"AIMA Chapter 7.2 The Wumpus World\", chapter7_2),\n",
        "  (\"AIMA Chapter 7.3 Logic\", chapter7_3),\n",
        "  (\"AIMA Chapter 7.4 Propositional Logic: A Very Simple Logic\", chapter7_4),\n",
        "  (\"AIMA 7.5 Propositional Theorem Proving\", chapter7_5),\n",
        "  (\"Module 7 Lecture Notes\", module7_lecture_notes),\n",
        "]\n",
        "\n",
        "module7_markdown = format_module(7, \"Logical Agents\", \n",
        "                         module7_sections, \n",
        "                         module7_definitions)\n",
        "display(Markdown(module7_markdown))"
      ],
      "execution_count": 56,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "# Module 7: Logical Agents\n## AIMA Chapter 7.1 Knowledge-Based Agents\n Your summary here \n\n## AIMA Chapter 7.2 The Wumpus World\n Your summary here \n\n## AIMA Chapter 7.3 Logic\n Your summary here \n\n## AIMA Chapter 7.4 Propositional Logic: A Very Simple Logic\n Your summary here \n\n## AIMA 7.5 Propositional Theorem Proving\n Your summary here \n\n## Module 7 Lecture Notes\n  PEAS: Specifying an Amazon delivery drone Performance measure:\nmaximize profits - minimize time - obey laws governing airspace restrictions - deliver package to right location - keep package in good condition - avoid accidents - reduce noise - preserve battery life\nEnvironment:      - airspace - obstacles when airborne (other drones, birds, buildings, trees, utility poles) - obstacles when landing (pets, patio furniture, lawnmowers, people, cars) - weather - distances/route information between warehouse and destinations - position of houses, and spaces that are safe for drop-off- package weight\n  \n   PEAS: Specifying an Amazon delivery drone Actuators:  - Propellers and flight control system- Payload actuators: E.g. Arm/basket/claw for picking up, dropping off packages- Lights or signals - Mechanism to announce/verify delivery- Device for delivering packages to customers\nSensors:   GPS - radar/Lidar- altitude sensor- weather sensors (barometer, etc). - gyroscope- accelerometer.  - camera   - rotor sensors.  - weight sensor to recognize package.   \n\n## Module 7 Definitions\n* **Concept** - definition\n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "T3cm1WV542_a",
        "outputId": "f8692140-6c21-4964-9a87-6db180fa4e42"
      },
      "source": [
        "# This will generate the full set of notes that you can use for your exam.\n",
        "full_markdown = \"\\n\\n\".join([module1_markdown, \n",
        "                             module2_markdown, \n",
        "                             module3_markdown, \n",
        "                             module4_markdown, \n",
        "                             module5_markdown,\n",
        "                             module6_markdown,\n",
        "                             module7_markdown,]\n",
        "                       )\n",
        "display(Markdown(full_markdown))"
      ],
      "execution_count": 57,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/markdown": "## WARNING: Your definition of 'agent program:' is too long!\nWARNING: Your definition of 'omniscience' is too long!\nWARNING: Your definition of 'Learning' is too long!\nWARNING: Your definition of 'autonomy' is too long!\nWARNING: Your definition of 'EPISODIC' is too long!\nWARNING: Your definition of 'SEMI-DYNAMIC' is too long!\n# Module 1: Rational Agents and Task Environments\n## AIMA Chapter 1.1 What is AI?\n Chapter 1.1 summary. \n\nAI is defined in two distinct ways : \nIntelligence can be defined in terms of fidelity to humans performance. (machine imitating humans).\nThere is another abstract definition of intelligence - called acting rationally. \nThese results in two fold dichotomy \nHuman vs Rational\nThought vs Behaviour\n\nSix disciplines of AI are:\n1. NLP\n2. Knowledge representation\n3. Automated reasoning\n4. Machine learning\n5. Total Turing test\n    5.1 Computer vision\n    5.2 Robotics\nThe quest for artifical flight succeeded when engineer and inventors stopped immitating bird and started using wind tunnels and learning about aerodynamics.Rational Agent : An agent that acts to achieve the best outcome or the best expected outcome if uncertainity is involved.\n\n\n## AIMA Chapter 27.1 The Limits of AI\n The Limits of AI - \n\n1. Weak AI - the idea that machines could act as if they were intelligent\n2. Strong AI - the assertion that machines that do so are actually comsciously thinking (not just simulating thinking) \nThis has morphed into human level AI or general AI - prgrams that can solve an arbitrarily wide variety of tasks.\n\n\n## AIMA Chapter 27.1 Can Machines Really Think?\n Can Machines really think - Computer are expected to simulate thinking.\nDo computers really have consciousness - are they self aware and aware of the outside world - are they aware of their own mortality or lack thereof.\nDo computers have qualia - the subjective experience of learning. Humans have a clear advantage over machines - hey can use their own subjective aparatus to appreciate the subjective experience of others. Machine however can run each others code.\n\n \n\n## AIMA 2.1 Agents and Environments\n A agent percieves its environment and acts upon that environment throught actuators. \n\n## AIMA 2.2 Good Behavior: The Concept of Rationality\n Rationality - doing the right things. \nConsequentialism - evaluating an agents behavior by its consequences. The sequence of actions causes the environment to go through a sequence of states. if the sequence is desirable, then the agent has performed well. This notion of desirability is captured by a performance measure.What is rational at any point depents on 1. Performance measure defining the criteria for success.\n2. The agents prior knowledge of the environment\n3. The actions that the agent can perform\n4. The agents percept sequence to date.\n\n\n## AIMA 2.3 The Nature of Environments\n Your summary here \n\n## AIMA 2.4 The Structure of Agents\n Your summary here \n\n## Module 1 Lecture Notes\n An agent is anything that can be viewed as\n• perceiving its environment through sensors and • acting upon that environment through actuators\nHuman agent:\n• Sensors: eyes, ears, ...\n• Actuators: hands, legs, mouth, ...\nRobotic agent:\n• Sensors: cameras and infrared range finders \n• Actuators: various motors\nAgents include humans, robots, softbots, thermostats, ...\n\nRational Agent:\n• For each possible percept sequence P\n• a rational agent selects an action a\n• to maximize its performance measure\n For each possible percept sequence P,\n• a rational agent selects an action a\n• to maximize its performance measure\n  Rational Agent (revised definition):\n• For each possible percept sequence P,\n• a rational agent selects an action a• that maximizes the expected value of its performance measure\nTo design a rational agent we need to specify a task environment\n• a problem specification for which the agent is a solution\nPEAS: to specify a task environment • Performance measure\n• Environment\n• Actuators\n• Sensors\n \n\n## Module 1 Definitions\n* **Agent** - anything that can perceive and act in an environment\n* **Percept** - Content that an agent's sensor are receiving\n* **Percept sequence** - is the complete history of everything the agent has ever received.\n* **Agent function** - the agent function maps any given percept sequence to an action.\n* **Agent program:** -  The agent functon will be implemented by an agent program - however they are distinct agent function is  an abstract mathematical description.The agent program is a concrete implmentation running in some physical system.\n* **Omniscience** - An omniscience agent knows the actual outcome of its actions and can act accordingly; but omniscience is actually impossible in reality.\n* **Information gathering** - Taking effective actions to modify future percepts is called information gathering\n* **Learning** - Information gathering is not enough, the agent needs to learn as much as possible from what it perceives. The agent's initial configuration couldreflect some prior knowledge of the environment, but as the agent gains more experience, this maybe modified and augmented.\n* **Autonomy** - The virtue of an rational agent to be self sufficient by  learning what it can to compensate for partial or incorrect prior knowledge.\n* **Episodic** - The agents experience is divided into atomic episodes - crucially the next episode does not depend on the actions taken in previous episodes\n* **Sequential** - In sequential TE, the current decision will impact all other furture decisions.\n* **Dynamic** - If the environment can change whie the agent is deliberating\n* **Static** - The environment is persisted in the same state while the agent is deliberating\n* **Semi-dynamic** - Environment itself does not change with the passage of time whilst the agent is deliberating but the agents performance score does.\n\n\n## WARNING: Your definition of 'list comprehension with filter' is too long!\nWARNING: Your definition of 'dictionary comprehension' is too long!\nWARNING: Your definition of 'dictionary comprehension2' is too long!\nWARNING: Your definition of 'set operations' is too long!\nWARNING: Your definition of 'generators key benefits' is too long!\n# Module 2: Python\n## Module 2 Lecture Notes\n \nFunctional level profiling:\no Rudimentary \nimport time\nt0 = time.time() \ncode_block\nt1 = time.time() \ntotal = t1-t0\n\no Timeit (more precise) \nimport timeit\nt = timeit.Timer(\"<statement to time>\", \"<setup code>\") \nt.timeit()\n▪ The second argument is usually an import that sets up a virtual environment for the statement\n▪ timeit calls the statement 1 million times and returns the total elapsed time, number argument specifies number of times to run it.\n\nScript level profiling \n\npython -m cProfile to_time.py\n\n\n## Module 2 Definitions\n* **Mutability** - In Python, 'mutable' is the ability of objects to change their values.\n* **Mutable objects** - Lists,Sets,Dictionaries,User-Defined Classes\n* **Immutable objects** - Numbers (Integer, Rational, Float, Decimal, Complex & Booleans)Strings,Tuples,Frozen Sets,User-Defined Classes\n* **Important** - #tuples are immutable, so you cannot add new elements, hence, using merge of tuples with the #\n* **Code1** - operator to add a new imaginary day in the tuple ‘weekdays’\n* **Code2** - weekdays = ‘Sunday’, ‘Monday’, ‘Tuesday’, ‘Wednesday’, ‘Thursday’, ‘Friday’, ‘Saturday’\n* **Code3** - weekdays  +=  ‘Pythonday’\n* **Horder 1** - def is_even(x): return x % 2 == 0\n* **Horder 2** - [i for i in [1, 2, 3, 4, 5] if is_even(i)][2, 4]\n* **Horder 3** - list(filter(is_even, [1, 2, 3, 4, 5])) #equivalent [2, 4]\n* **Horder 4** - list(filter(lambda x: x%2==0, [1, 2, 3, 4, 5])) #also equivalent\n* **Complex loop** - for (x, y) in [('a',1), ('b',2), ('c',3), ('d',4)]:\n* **Complex loop1** -              print(x)\n* **Subclasses** - class AI_Student(Student):\n* **Subclasses2** - fav_class = CIS 521\n* **List comprehension** -  squares = [x*x for x in nums]\n* **List comprehension with filter** -  li = [3, 6, 2, 7, 1, 9];[elem * 2 for elem in li if elem > 4] #[12, 14, 18]\n* **Dictionary comprehension** - dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}; # Check for items greater than 2 ;dict1_cond = {k:v for (k,v) in dict1.items() if v>2 if v%2 == 0}\n* **Dictionary comprehension2** - dict1 = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f':6} # Identify odd and even entries#dict1_tripleCond = {k:('even' if v%2==0 else 'odd') for (k,v) in dict1.items()}\n* **Nested dictionary comp** - nested_dict = {'first':{'a':1}, 'second':{'b':2}}#float_dict = {outer_k: {float(inner_v) for (inner_k, inner_v) in outer_v.items()} for (outer_k, outer_v) in nested_dict.items()}#print(float_dict)\n* **List operations** - append(value), index(value), remove(value), count(value), a.reverse(), a.sort, b_list = sorted(a)\n* **Set operations** - add, remove, discard(silent), clear,pop,UNION x1.union(x2) or x1 | x2, INTERSECTION: x1.intersection(x2) or x1 & x2, MINUS x1.difference(x2) or x1 - x2\n* **Set operations2** - Update x1.update(x1,x2...),SYMMETRIC difference :x1.symmetric_difference(x2) or x1^x2\n* **Invalid set operations1** - a = [1, 2, 3]; {a} # TypeError: unhashable type: 'list'\n* **Invalid set operations2** - d = {'a': 1, 'b': 2}; {d} TypeError: unhashable type: 'dict'\n* **Dict operations** - dict.keys(), dict.values(), dict[key] = value\n* **Frozen_set** - frozen sets are immutable x = frozenset(['foo', 'bar', 'ba'])\n* **__init__** - (duck typing) The constructor for the class.\n* **__len__** - Define how len( obj ) works.\n* **__copy__** - Define how to copy a class.\n* **__cmp__** - Define how == works for class.\n* **__add__** - Define how + works for class\n* **__neg__** - Define how unary negation works for class\n* **__iter__** - Denotes if an oject is iterable iterable: __iter__ method which returns an iterator obj\n* **__next__** - Denotes if the object is a iterator : returns the next object in the sequence\n* **Generators** - Generators are iterators (with __next()__ method)\n* **Generators key benefits** - ▪ Less code than writing a standard iterator▪ Maintains local state automatically▪ Values are computed one at a time, as they’re needed▪ Avoids storing the entire sequence in memory▪ Good for aggregating (summing, counting) items. One pass.▪ Crucial for infinite sequences▪ Bad if you need to inspect the individual values\n* **Yield** - Functions that contain the yield keyword automatically return a generator when called\n* **Generators are better1** - Review: generators are good for aggregating items.\n* **Generators are better2** - sum(x for x in xrange(10**8) if x%5==0) #999999950000000L (Generator)\n* **Generators are better3** - Than version this which creates the whole list first\n* **Generators are better4** - sum([x for x in xrange(10**8) if x%5==0]) 999999950000000L (List comprehension)\n\n\n## WARNING: Your definition of 'search' is too long!\nWARNING: Your definition of 'Closed Loop' is too long!\n# Module 3: Search Problems\n## AIMA Chapter 3.1 Problem-Solving Agents\n Goal Formulation, problem forumulation, Search, Execution \n\n## AIMA Chapter 3.2 Example Problems\n Your summary here \n\n## AIMA Chapter 3.3 Search Algorithms\n A search algorithm takes a search problem as input and returns a solution or an indication of failure \n\n## AIMA Chapter 3.4 Uninformed Search Strategies\n Your summary here \n\n## Module 3 Lecture Notes\n \n\nfunction AC-3(csp) return the CSP, possibly with reduced domains\ninputs: csp, a binary csp with variables {X1, X2, …, Xn}\nlocal variables: queue, a queue of arcs initially the arcs in csp\nwhile queue is not empty do\n(Xi,Xj) ¬ queue.pop()\nif REMOVE-INCONSISTENT-VALUES(Xi\n, Xj\n) then\nfor each Xk in NEIGHBORS[Xi ] – {Xj} do\nadd (Xk, Xi\n) to queue\nfunction REMOVE-INCONSISTENT-VALUES(Xi\n, Xj\n) return true iff we remove a value\nremoved ¬ false\nfor each x in DOMAIN[Xi\n] do\nif no value y in DOMAIN[Xj\n] allows (x,y) to satisfy the constraints between Xi and Xj\nthen delete x from DOMAIN[Xi\n]; removed ¬ true\nreturn removed\n\n\n\n## Module 3 Definitions\n* **Problem-solving agent** - an agent that can formulate a plan via search\n* **Search** - Before taking any action in the real world. Thea gent simulates sequence of actions in its model,searching until it finds a sequence of actions that reaches the goal. Such a sequence is called a solution.\n* **Execution** - The agent can now execute the actins in the solution, one at a time.\n* **State space** - A set of possible states the environment an be in.\n* **Inital states** -  & Goal states.\n* **Transition model** -  A transition model describes what each action does Result(Arad,ToZerind) = Zerind.\n* **Action cost function** - Numerical cost of applying action a in the state s to reach state s prime.\n* **Open loop** - In a open loop system, once the agent has found a solution - it will execute action blindly.\n* **Closed loop** - In a closed loop system, the agent monitors its perceots and engages actively with the decision making model to make changes due to non deterministic nature of its environment.\n* **Optimal solution** - A solution with the lowest path cost among all solutions.\n\n\n# Module 4: Informed Search\n## AIMA Chapter 3.5 Informed (Heuristic) Search Strategies\n Your summary here \n\n## AIMA Chapter 3.6 Heuristic Functions\n Your summary here \n\n## Module 4 Lecture Notes\n Your summary here \n\n## Module 4 Definitions\n* **Concept** - definition\n\n\n# Module 5: Adversarial Search and Games\n## AIMA Chapter 5.1 Game Theory\n Your summary here \n\n## AIMA Chapter 5.2 Optimal Decisions in Games\n Your summary here \n\n## AIMA Chapter 5.3 Heuristic Alpha–Beta Tree Search\n Your summary here \n\n## AIMA Chapter 5.5 Stochastic Games\n Your summary here \n\n## AIMA Chapter 16.1 Combining Beliefs and Desires under Uncertainty\n Your summary here \n\n## AIMA Chapter 16.2 The Basis of Utility Theory\n Your summary here \n\n## AIMA Chapter 16.3 Utility Functions\n Your summary here \n\n## Module 5 Lecture Notes\n\n```\n############################################################\n# Section 1: Dominoes Game - A* Search\n############################################################\n\ndef create_dominoes_game(rows, cols):\n    new_board = [[False for j in range(cols)] for i in range(rows)]\n    return DominoesGame(new_board)\n\n\nclass DominoesGame(object):\n\n    # Required\n    def __init__(self, board):\n        self.board = board[:]\n        self.row_count = len(board)\n        if len(board[0]) != 0:\n            self.col_count = len(board[0])\n        else:\n            self.col_count = 0\n\n    def reset(self):\n        for i in range(self.row_count):\n            for j in range(self.col_count):\n                self.board[i][j] = False\n\n    def get_board(self):\n        return self.board\n\n    def is_legal_move(self, row, col, vertical):\n        if vertical:\n            if 0 <= row < self.row_count - 1 and col < self.col_count:\n                return not (self.board[row][col] or self.board[row + 1][col])\n            else:\n                return False\n        else:\n            if 0 <= col < self.col_count - 1 and row < self.row_count:\n                return not (self.board[row][col] or self.board[row][col + 1])\n            else:\n                return False\n\n    def legal_moves(self, vertical):\n        moves = []\n        for row in range(self.row_count):\n            for col in range(self.col_count):\n                if self.is_legal_move(row, col, vertical):\n                    yield (row, col)\n\n    def perform_move(self, row, col, vertical):\n        if self.is_legal_move(row, col, vertical):\n            self.board[row][col] = True\n            if vertical:\n                self.board[row + 1][col] = True\n            else:\n                self.board[row][col + 1] = True\n\n    def game_over(self, vertical):\n        moves = list(self.legal_moves(vertical))\n        return len(moves) == 0\n\n    def copy(self):\n        return DominoesGame([row[:] for row in self.board])\n\n    def successors(self, vertical):\n        for x, y in self.legal_moves(vertical):\n            new_board = self.copy()\n            new_board.perform_move(x, y, vertical)\n            yield (x, y), new_board\n\n    def get_random_move(self, vertical):\n        return random.choice(list(self.legal_moves(vertical)))\n\n    def ab_max_value(self, alpha, beta, current_loc, limit, vertical):\n        left = list(self.successors(vertical))\n        right = list(self.successors(not vertical))\n\n        if limit == 0 or self.game_over(vertical):\n            return current_loc, len(left) - len(right), 1\n\n        # start with negative infinity to find the max value.\n        max_value = -math.inf\n\n        state = 0\n        curr_move = current_loc\n        for loc, next_board in left:\n            # switch between vertical and horizontal plays at each recursive call\n            move, temp, count = next_board.ab_min_value(alpha=alpha, beta=beta, current_loc=loc,                                                    limit=limit - 1, vertical=not vertical)\n            state = state + count\n\n            if temp > max_value:\n                curr_move = loc\n                max_value = temp\n            if max_value >= beta:\n                return curr_move, max_value, state\n\n            alpha = max(alpha, max_value)\n\n        return curr_move, max_value, state\n\n    def ab_min_value(self, alpha, beta, current_loc, limit, vertical):\n        right = list(self.successors(not vertical))\n        left = list(self.successors(vertical))\n\n        if limit == 0 or self.game_over(vertical):\n            return current_loc, len(right) - len(left), 1\n\n        state = 0\n        # start with postitive infinity to find the min value\n        min_value = math.inf\n        curr_move = current_loc\n\n        for loc, next_board in left:\n            # switch between vertical and horizontal plays at each recursive call\n            move, temp, count = next_board.ab_max_value(alpha=alpha, beta=beta, current_loc=loc,                                                    limit=limit - 1, vertical=not vertical)\n            state = state + count\n\n            if temp < min_value:\n                curr_move = loc\n                min_value = temp\n            if min_value <= alpha:\n                return curr_move, min_value, state\n\n            beta = min(beta, min_value)\n\n        return curr_move, min_value, state\n\n    def get_best_move(self, vertical, limit):\n        return self.ab_max_value(alpha=-math.inf, beta=math.inf, moves=None, limit=limit, vertical=vertical)\n\n    # Required\n    def get_best_move(self, vertical, limit):\n        return self.ab_max_value(alpha=-math.inf, beta=math.inf,                                  current_loc=None, limit=limit, vertical=vertical)\n```\n \n\n## Module 5 Definitions\n* **Concept** - definition\n\n\n## WARNING: Your summary of 'Module 6 Lecture Notes' is too long!\n## WARNING: Your definition of 'Inference' is too long!\n# Module 6: Constraint Satisfaction Problems\n## AIMA Chapter 6.1 Defining Constraint Satisfaction Problems\n A problem is solved when each variable has a value that \n\n## AIMA Chapter 6.2 Constraint Propagation: Inference in CSPs\n Your summary here \n\n## AIMA Chapter 6.3 Backtracking Search for CSPs\n Your summary here \n\n## AIMA Chapter 6.4 Local Search for CSPs\n Your summary here \n\n## AIMA Chapter 6.5 The Structure of Problems\n Your summary here \n\n## Module 6 Lecture Notes\nA CSP consists of:\n1. X : Finite set of variables X1, X2, ..., Xn\n2. D : Nonempty domain of possible values for each variable\nD1, D2, ... Dn where Di = {v1, ..., vk}\n3. C: Finite set of constraints C1, C2, ..., Cm\n4. Each constraint Ci limits the values that variables can take, e.g., X1 ≠ X2 A state is defined as an assignment of values to some or all variables.\n5. A consistent assignment does not violate the constraints. \n Example problem: Sudoku\n An assignment is complete when every variable is assigned a value.\n A solution to a CSP is a complete, consistent assignment.\n Solutions to CSPs can be found by a completely general purpose algorithm, given only the formal specification of the CSP.\n Beyond our scope: CSPs that require a solution that maximizes an objective function.\n\n Assumptions about the world: a single agent, deterministic actions, fully observed state, discrete state space\n Planning: sequences of actions\n▪ The path to the goal is the important thing\n▪ Paths have various costs, depths\n▪ Heuristics give problem-specific guidance\nIdentification: assignments to variables\n▪ The goal itself is important, not the path\n▪ All paths at the same depth (for some formulations)\n▪ CSPs are specialized for identification problems\n \nWhen to Iterate, When to Stop?\nThe crucial principle:\nIf a value is removed from a node Xi,\nthen the values on all of Xi’s neighbors must be reexamined.\n\nWhy? Removing a value from a node may result in one of the neighbors becoming arc inconsistent, so we need to check...\n(but each neighbor Xj can only become inconsistent with respect to the removed values on Xi)\n\nA CSP can easily be expressed as a search problem\n▪ Initial State: the empty assignment {}.\n▪ Successor function: Assign value to any unassigned variable provided that there is not a constraint conflict.\n▪ Goal test: the current assignment is complete.\n▪ Path cost: a constant cost for every step.\no Solution is always found at depth n, for n variables\n▪ Hence Depth First Search can be used\n\n\nQuestion 8\nIn arc consistency checks we have a pair of nodes, X (in the tail of the arc) and Y (at the head of the arc).  \nIn order to make X arc-consistent with Y, which of the following is true (assuming only inequality constraints):\n\n1. We must check all values in X\n2. Remove values from X that are present in Y\n3. In arc consistency checks, we check all values in the tail and remove any that are not allowed by the head.  We always check all values on the tail, \nbut we can stop checking values on the head when we first see a value that allows the value that we’re checking on the tail.\n\nAC-3 can solve all solvable sudoku problems in O(n^n +1 ) time.\n\n```\n# Include your imports here, if any are used.\nimport copy\nimport queue\nimport collections\n\n\n############################################################\n# Sudoku Solver\n############################################################\n\ndef sudoku_cells():\n    return [(i, j) for i in range(9) for j in range(9)]\n\n\ndef get_grid_hash(r1, c1):\n    row_index = (int)(r1 / 3)\n    col_index = (int)(c1 / 3)\n\n    # grid_hash - to map cells to unique grids\n    return 3 * row_index + col_index\n\n\ndef sudoku_grids():\n    grids = {}\n    for r1 in range(9):\n        for c1 in range(9):\n            grids[(r1, c1)] = get_grid_hash(r1, c1)\n    return grids\n\n\ndef sudoku_arcs():\n    grids = sudoku_grids()\n    arcs = set()\n    for r1, c1 in sudoku_cells():\n        for r2, c2 in sudoku_cells():\n            # verify if the cells are in the same constraint group - same row or same column or grid\n            if (r1 == r2 or c1 == c2 or grids[(r1, c1)] == grids[(r2, c2)])                     and (r1, c1) != (r2, c2):  # should not be the same cell\n                arcs.add(((r1, c1), (r2, c2)))\n    return arcs\n\n\ndef read_board(path):\n    with open(path) as file:\n        board = file.readlines()  # read all the lines at once.\n    board_dict = dict()\n    for r1 in range(9):\n        for c1 in range(9):\n            if board[r1][c1] == '*':\n                board_dict[(r1, c1)] = set(list(range(1, 10)))  # * indicates (1-9) are open to be selected\n            else:\n                board_dict[(r1, c1)] = {int(board[r1][c1])}  # cell value is given and fixed.\n    return board_dict\n\n\nclass Sudoku(object):\n    CELLS = sudoku_cells()\n    ARCS = sudoku_arcs()\n    GRIDS = sudoku_grids()\n\n    def __init__(self, board):\n        self.board = board\n\n    def get_values(self, cell):\n        return self.board[cell]\n\n    def get_arc_neighbours(self, cell1):\n        return set([arc for arc in Sudoku.ARCS if arc[0] == cell1 or arc[1] == cell1])\n\n    def get_cell_neighbours(self, cell1):\n        arc_neighbors = set([arc for arc in Sudoku.ARCS if arc[0] == cell1 or arc[1] == cell1])\n        cell_neighbors = set()\n        for arc in arc_neighbors:\n            if arc[0] == cell1:\n                cell_neighbors.add(arc[1])\n            elif arc[1] == cell1:\n                cell_neighbors.add(arc[0])\n        return cell_neighbors\n\n    def is_solved(self):\n        for cell1, cell2 in self.ARCS:\n            if len(self.board[cell1]) != 1 or len(self.board[cell2]) != 1 or self.board[cell1] == self.board[cell2]:\n                return False\n        return True\n\n    def remove_inconsistent_values(self, cell1, cell2):\n        removed = False\n        if cell1 == cell2:\n            return removed\n\n        inconsistent_values = set()\n\n        for x in self.board[cell1]:\n            # if x is inconsistent with all values in cell2, add x to the inconsistent_values\n            cell2_values = self.get_values(cell2)\n            if x in cell2_values and len(cell2_values) == 1:\n                inconsistent_values.add(x)\n\n        # remove inconsistent values from cell 1\n        data = self.get_values(cell1)\n        for y in inconsistent_values:\n            data.remove(y)\n\n        return len(inconsistent_values) > 0\n\n    def infer_ac3(self):\n        queue = collections.deque(Sudoku.ARCS)\n        while queue:\n            (cell1, cell2) = queue.popleft()\n            if self.remove_inconsistent_values(cell1, cell2):\n                neighbours = self.get_arc_neighbours(cell1)\n                for cell in neighbours:\n                    queue.append(cell)\n        return self.is_solved()\n\n    def get_constrained_values(self, cell1):\n        neighbors = self.get_cell_neighbours(cell1)\n        constrained_vals = []\n        for cell in neighbors:\n            for value in self.board.get(cell):\n                constrained_vals.append(value)\n        return constrained_vals\n\n    def get_guesses(self):\n        for cell in self.CELLS:\n            if len(self.board[cell]) > 1:\n                for x in self.board[cell]:\n                    option = copy.deepcopy(self.board)\n                    option[cell] = {x}\n                    yield option\n                break\n\n    def infer_improved(self):\n        additional_inference_made = True\n        while additional_inference_made:\n            self.infer_ac3()\n\n            additional_inference_made = False\n\n            for cell in Sudoku.CELLS:\n                options = self.board[cell]\n                if len(options) > 1:\n                    if self.revise_constrained_cells(cell):\n                        additional_inference_made = True\n                        continue\n        return self.is_solved()\n\n    def infer_with_guessing(self):\n        boards = queue.deque([copy.deepcopy(self.board)])\n        while len(boards) > 0:\n            guess = boards.popleft()\n            self.board = guess\n            if self.infer_improved():\n                break\n            else:\n                boards += list(self.get_guesses())\n\n    def check_consistency(self, cell, neighbors):\n        for x in self.board[cell]:\n            # assume this value is correct\n            consistent = True\n            for cell in neighbors:\n                if x in self.board[cell]:\n                    consistent = False\n                    break\n\n            # return the unique value x\n            if consistent:\n                return True, set([x])\n        return False, None\n\n    def revise_constrained_cells(self, cell1):\n        revised = False\n        if len(self.board[cell1]) > 1:\n            same_row = set()\n            same_col = set()\n            same_grid = set()\n\n            for cell2 in self.CELLS:\n                if cell1 != cell2:\n                    if self.GRIDS[cell1] == self.GRIDS[cell2]:  # same 3 X 3 grid\n                        same_grid.add(cell2)\n                    if cell1[0] == cell2[0]:  # same row\n                        same_row.add(cell2)\n                    if cell1[1] == cell2[1]:  # same col\n                        same_col.add(cell2)\n\n            # process each constriant group one at a time - to ease into an optimal solution iteratively\n\n            # process grid constraint arcs as the first preference\n            replace_cell, cell = self.check_consistency(cell1, same_grid)\n            if replace_cell:\n                self.board[cell1] = cell\n                return True\n\n            replace_cell, cell = self.check_consistency(cell1, same_row)\n\n            # process row constraint arcs as the second preference\n            if replace_cell:\n                self.board[cell1] = cell\n                return True\n\n            # process col constraint arcs as the third preference\n            replace_cell, cell = self.check_consistency(cell1, same_col)\n            if replace_cell:\n                self.board[cell1] = cell\n                return True\n\n        return revised\n\n```\n\n  \n\n## Module 6 Definitions\n* **Inference** - Using problem constraints to remove the number of legal values on variables. CSPs allow for a mixture of search (expanding a node to visit its successors) and inference, where the current choice of nodes and values are reduced by propagating constraints.\n* **Declarative programming** - Representing a problem formally (without regards to implementation) as a constraint satisfaction problem is an instance of Declarative prog\n* **Chapter6_5** - \n\n\n# Module 7: Logical Agents\n## AIMA Chapter 7.1 Knowledge-Based Agents\n Your summary here \n\n## AIMA Chapter 7.2 The Wumpus World\n Your summary here \n\n## AIMA Chapter 7.3 Logic\n Your summary here \n\n## AIMA Chapter 7.4 Propositional Logic: A Very Simple Logic\n Your summary here \n\n## AIMA 7.5 Propositional Theorem Proving\n Your summary here \n\n## Module 7 Lecture Notes\n  PEAS: Specifying an Amazon delivery drone Performance measure:\nmaximize profits - minimize time - obey laws governing airspace restrictions - deliver package to right location - keep package in good condition - avoid accidents - reduce noise - preserve battery life\nEnvironment:      - airspace - obstacles when airborne (other drones, birds, buildings, trees, utility poles) - obstacles when landing (pets, patio furniture, lawnmowers, people, cars) - weather - distances/route information between warehouse and destinations - position of houses, and spaces that are safe for drop-off- package weight\n  \n   PEAS: Specifying an Amazon delivery drone Actuators:  - Propellers and flight control system- Payload actuators: E.g. Arm/basket/claw for picking up, dropping off packages- Lights or signals - Mechanism to announce/verify delivery- Device for delivering packages to customers\nSensors:   GPS - radar/Lidar- altitude sensor- weather sensors (barometer, etc). - gyroscope- accelerometer.  - camera   - rotor sensors.  - weight sensor to recognize package.   \n\n## Module 7 Definitions\n* **Concept** - definition\n",
            "text/plain": [
              "<IPython.core.display.Markdown object>"
            ]
          },
          "metadata": {}
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "vX7lbnJd4sT5"
      },
      "source": [
        "# Your Exam Notes\n",
        "\n",
        "Here are your full set of exam notes that you can use on your midterm.  Choose **Runtime > Run all**, then your notes will be displayed.\n",
        "\n",
        "If you are taking the exam in person, then you must print a copy of your notes for yourself.   Here's how to print:\n",
        "* Run the cell below. \n",
        "* Click its \"Mirror cell in tab\" button (this is located in the upper right corner of the cell, immediately to the right of the trash can).  This will open the cell.  \n",
        "* In Chrome, you can then print your notes using the **File > Print** option from the browser's menu.  \n",
        "* In the print options, change the layout from Landscape to Portrait. \n",
        "* Clicking the Save, will save a PDF that you can then print.\n"
      ]
    }
  ]
}