From 59414271cbbc35413d2a4386f14d966ec9226579 Mon Sep 17 00:00:00 2001 From: Leandro Casuso Montero <32684478+casuso@users.noreply.github.com> Date: Sun, 16 Sep 2018 22:17:16 -0700 Subject: [PATCH 01/15] Remove unnecessary goal test in search.py (#953) Remove unnecessary initial goal test in best_first_graph_search. The loop will catch that case immediately. --- search.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/search.py b/search.py index 0504fba59..aa556c3a0 100644 --- a/search.py +++ b/search.py @@ -263,8 +263,6 @@ def best_first_graph_search(problem, f): a best first search you can examine the f values of the path returned.""" f = memoize(f, 'f') node = Node(problem.initial) - if problem.goal_test(node.state): - return node frontier = PriorityQueue('min', f) frontier.append(node) explored = set() From 6295960eb61cf5a76795c06540070a0eedd6c3e5 Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Wed, 19 Sep 2018 03:29:32 +0500 Subject: [PATCH 02/15] Minor Changes in Text (#955) --- neural_nets.ipynb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/neural_nets.ipynb b/neural_nets.ipynb index 9c5db9a56..ecdeedcde 100644 --- a/neural_nets.ipynb +++ b/neural_nets.ipynb @@ -32,13 +32,13 @@ "\n", "### Overview\n", "\n", - "Although the Perceptron may seem like a good way to make classifications, it is a linear classifier (which, roughly, means it can only draw straight lines to divide spaces) and therefore it can be stumped by more complex problems. We can extend Perceptron to solve this issue, by employing multiple layers of its functionality. The construct we are left with is called a Neural Network, or a Multi-Layer Perceptron, and it is a non-linear classifier. It achieves that by combining the results of linear functions on each layer of the network.\n", + "Although the Perceptron may seem like a good way to make classifications, it is a linear classifier (which, roughly, means it can only draw straight lines to divide spaces) and therefore it can be stumped by more complex problems. To solve this issue we can extend Perceptron by employing multiple layers of its functionality. The construct we are left with is called a Neural Network, or a Multi-Layer Perceptron, and it is a non-linear classifier. It achieves that by combining the results of linear functions on each layer of the network.\n", "\n", - "Similar to the Perceptron, this network also has an input and output layer. However it can also have a number of hidden layers. These hidden layers are responsible for the non-linearity of the network. The layers are comprised of nodes. Each node in a layer (excluding the input one), holds some values, called *weights*, and takes as input the output values of the previous layer. The node then calculates the dot product of its inputs and its weights and then activates it with an *activation function* (sometimes a sigmoid). Its output is fed to the nodes of the next layer. Note that sometimes the output layer does not use an activation function, or uses a different one from the rest of the network. The process of passing the outputs down the layer is called *feed-forward*.\n", + "Similar to the Perceptron, this network also has an input and output layer; however, it can also have a number of hidden layers. These hidden layers are responsible for the non-linearity of the network. The layers are comprised of nodes. Each node in a layer (excluding the input one), holds some values, called *weights*, and takes as input the output values of the previous layer. The node then calculates the dot product of its inputs and its weights and then activates it with an *activation function* (e.g. sigmoid activation function). Its output is then fed to the nodes of the next layer. Note that sometimes the output layer does not use an activation function, or uses a different one from the rest of the network. The process of passing the outputs down the layer is called *feed-forward*.\n", "\n", - "After the input values are fed-forward into the network, the resulting output can be used for classification. The problem at hand now is how to train the network (ie. adjust the weights in the nodes). To accomplish that we utilize the *Backpropagation* algorithm. In short, it does the opposite of what we were doing up to now. Instead of feeding the input forward, it will feed the error backwards. So, after we make a classification, we check whether it is correct or not, and how far off we were. We then take this error and propagate it backwards in the network, adjusting the weights of the nodes accordingly. We will run the algorithm on the given input/dataset for a fixed amount of time, or until we are satisfied with the results. The number of times we will iterate over the dataset is called *epochs*. In a later section we take a detailed look at how this algorithm works.\n", + "After the input values are fed-forward into the network, the resulting output can be used for classification. The problem at hand now is how to train the network (i.e. adjust the weights in the nodes). To accomplish that we utilize the *Backpropagation* algorithm. In short, it does the opposite of what we were doing up to this point. Instead of feeding the input forward, it will track the error backwards. So, after we make a classification, we check whether it is correct or not, and how far off we were. We then take this error and propagate it backwards in the network, adjusting the weights of the nodes accordingly. We will run the algorithm on the given input/dataset for a fixed amount of time, or until we are satisfied with the results. The number of times we will iterate over the dataset is called *epochs*. In a later section we take a detailed look at how this algorithm works.\n", "\n", - "NOTE: Sometimes we add to the input of each layer another node, called *bias*. This is a constant value that will be fed to the next layer, usually set to 1. The bias generally helps us \"shift\" the computed function to the left or right." + "NOTE: Sometimes we add another node to the input of each layer, called *bias*. This is a constant value that will be fed to the next layer, usually set to 1. The bias generally helps us \"shift\" the computed function to the left or right." ] }, { @@ -60,7 +60,7 @@ "\n", "The NeuralNetLearner returns the `predict` function which, in short, can receive an example and feed-forward it into our network to generate a prediction.\n", "\n", - "In more detail, the example values are first passed to the input layer and then they are passed through the rest of the layers. Each node calculates the dot product of its inputs and its weights, activates it and pushes it to the next layer. The final prediction is the node with the maximum value from the output layer." + "In more detail, the example values are first passed to the input layer and then they are passed through the rest of the layers. Each node calculates the dot product of its inputs and its weights, activates it and pushes it to the next layer. The final prediction is the node in the output layer with the maximum value." ] }, { @@ -80,7 +80,7 @@ "\n", "### Overview\n", "\n", - "In both the Perceptron and the Neural Network, we are using the Backpropagation algorithm to train our weights. Basically it achieves that by propagating the errors from our last layer into our first layer, this is why it is called Backpropagation. In order to use Backpropagation, we need a cost function. This function is responsible for indicating how good our neural network is for a given example. One common cost function is the *Mean Squared Error* (MSE). This cost function has the following format:\n", + "In both the Perceptron and the Neural Network, we are using the Backpropagation algorithm to train our model by updating the weights. This is achieved by propagating the errors from our last layer (output layer) back to our first layer (input layer), this is why it is called Backpropagation. In order to use Backpropagation, we need a cost function. This function is responsible for indicating how good our neural network is for a given example. One common cost function is the *Mean Squared Error* (MSE). This cost function has the following format:\n", "\n", "$$MSE=\\frac{1}{n} \\sum_{i=1}^{n}(y - \\hat{y})^{2}$$\n", "\n", @@ -169,7 +169,7 @@ "source": [ "### Implementation\n", "\n", - "First, we feed-forward the examples in our neural network. After that, we calculate the gradient for each layer weights. Once that is complete, we update all the weights using gradient descent. After running these for a given number of epochs, the function returns the trained Neural Network." + "First, we feed-forward the examples in our neural network. After that, we calculate the gradient for each layers' weights by using the chain rule. Once that is complete, we update all the weights using gradient descent. After running these for a given number of epochs, the function returns the trained Neural Network." ] }, { @@ -206,9 +206,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The output should be 0, which means the item should get classified in the first class, \"setosa\". Note that since the algorithm is non-deterministic (because of the random initial weights) the classification might be wrong. Usually though it should be correct.\n", + "The output should be 0, which means the item should get classified in the first class, \"setosa\". Note that since the algorithm is non-deterministic (because of the random initial weights) the classification might be wrong. Usually though, it should be correct.\n", "\n", - "To increase accuracy, you can (most of the time) add more layers and nodes. Unfortunately the more layers and nodes you have, the greater the computation cost." + "To increase accuracy, you can (most of the time) add more layers and nodes. Unfortunately, increasing the number of layers or nodes also increases the computation cost and might result in overfitting." ] } ], From 8a6cb3d9443ebb7942aa6967b3cadc9ac671657b Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Wed, 19 Sep 2018 08:42:02 +0500 Subject: [PATCH 03/15] Minor text change (#957) To make it more accurate. --- agents.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agents.ipynb b/agents.ipynb index 023de8021..10cecda7e 100644 --- a/agents.ipynb +++ b/agents.ipynb @@ -95,7 +95,7 @@ "\n", "class Park(Environment):\n", " def percept(self, agent):\n", - " '''prints & return a list of things that are in our agent's location'''\n", + " '''prints & return a list of things that are in our agent's surrounding environemnt'''\n", " things = self.list_things_at(agent.location)\n", " return things\n", " \n", From a28bf5a491c545badddfab78855b98528c4b159a Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Wed, 19 Sep 2018 08:42:28 +0500 Subject: [PATCH 04/15] Minor change in text (#956) To make it more descriptive and accurate. --- agents.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agents.ipynb b/agents.ipynb index 10cecda7e..02634439d 100644 --- a/agents.ipynb +++ b/agents.ipynb @@ -39,7 +39,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "What we have just done is create a dog who can only feel what's in his location (since he's blind), and can eat or drink. Let's see if he's alive..." + "What we have just done is create a dog who can only feel what's in his surrounding environment (since he's blind), and can eat or drink. Let's see if he's alive..." ] }, { From 3a833359cfaba5e7bbd3195976e76fda4b94030b Mon Sep 17 00:00:00 2001 From: Nouman Ahmed <35970677+Noumanmufc1@users.noreply.github.com> Date: Thu, 20 Sep 2018 03:33:26 +0500 Subject: [PATCH 05/15] Added relu Activation (#960) * added relu activation * added default parameters --- learning.py | 31 ++-- neural_nets.ipynb | 351 ++++++++++++++++++++++++++++++++++++++++++++-- utils.py | 10 +- 3 files changed, 367 insertions(+), 25 deletions(-) diff --git a/learning.py b/learning.py index 20e47d05b..399654073 100644 --- a/learning.py +++ b/learning.py @@ -4,7 +4,7 @@ removeall, unique, product, mode, argmax, argmax_random_tie, isclose, gaussian, dotproduct, vector_add, scalar_vector_product, weighted_sample_with_replacement, weighted_sampler, num_or_str, normalize, clip, sigmoid, print_table, - open_data, sigmoid_derivative, probability, norm, matrix_multiplication + open_data, sigmoid_derivative, probability, norm, matrix_multiplication, relu, relu_derivative ) import copy @@ -652,7 +652,7 @@ def predict(example): def NeuralNetLearner(dataset, hidden_layer_sizes=None, - learning_rate=0.01, epochs=100): + learning_rate=0.01, epochs=100, activation = sigmoid): """Layered feed-forward network. hidden_layer_sizes: List of number of hidden units per hidden layer learning_rate: Learning rate of gradient descent @@ -664,9 +664,9 @@ def NeuralNetLearner(dataset, hidden_layer_sizes=None, o_units = len(dataset.values[dataset.target]) # construct a network - raw_net = network(i_units, hidden_layer_sizes, o_units) + raw_net = network(i_units, hidden_layer_sizes, o_units, activation) learned_net = BackPropagationLearner(dataset, raw_net, - learning_rate, epochs) + learning_rate, epochs, activation) def predict(example): # Input nodes @@ -695,7 +695,7 @@ def random_weights(min_value, max_value, num_weights): return [random.uniform(min_value, max_value) for _ in range(num_weights)] -def BackPropagationLearner(dataset, net, learning_rate, epochs): +def BackPropagationLearner(dataset, net, learning_rate, epochs, activation=sigmoid): """[Figure 18.23] The back-propagation algorithm for multilayer networks""" # Initialise weights for layer in net: @@ -743,8 +743,11 @@ def BackPropagationLearner(dataset, net, learning_rate, epochs): # Error for the MSE cost function err = [t_val[i] - o_nodes[i].value for i in range(o_units)] - # The activation function used is the sigmoid function - delta[-1] = [sigmoid_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] + # The activation function used is relu or sigmoid function + if node.activation == sigmoid: + delta[-1] = [sigmoid_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] + else: + delta[-1] = [relu_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] # Backward pass h_layers = n_layers - 2 @@ -756,7 +759,11 @@ def BackPropagationLearner(dataset, net, learning_rate, epochs): # weights from each ith layer node to each i + 1th layer node w = [[node.weights[k] for node in nx_layer] for k in range(h_units)] - delta[i] = [sigmoid_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) + if activation == sigmoid: + delta[i] = [sigmoid_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) + for j in range(h_units)] + else: + delta[i] = [relu_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) for j in range(h_units)] # Update weights @@ -800,14 +807,14 @@ class NNUnit: weights: Weights to incoming connections """ - def __init__(self, weights=None, inputs=None): + def __init__(self, activation=sigmoid, weights=None, inputs=None): self.weights = weights or [] self.inputs = inputs or [] self.value = None - self.activation = sigmoid + self.activation = activation -def network(input_units, hidden_layer_sizes, output_units): +def network(input_units, hidden_layer_sizes, output_units, activation=sigmoid): """Create Directed Acyclic Network of given number layers. hidden_layers_sizes : List number of neuron units in each hidden layer excluding input and output layers @@ -818,7 +825,7 @@ def network(input_units, hidden_layer_sizes, output_units): else: layers_sizes = [input_units] + [output_units] - net = [[NNUnit() for n in range(size)] + net = [[NNUnit(activation) for n in range(size)] for size in layers_sizes] n_layers = len(net) diff --git a/neural_nets.ipynb b/neural_nets.ipynb index ecdeedcde..fe632c27f 100644 --- a/neural_nets.ipynb +++ b/neural_nets.ipynb @@ -14,9 +14,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from learning import *\n", @@ -65,9 +63,148 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " Codestin Search App\n", + " \n", + " \n", + "\n", + "\n", + "

\n", + "\n", + "
def NeuralNetLearner(dataset, hidden_layer_sizes=None,\n",
+       "                     learning_rate=0.01, epochs=100, activation = sigmoid):\n",
+       "    """Layered feed-forward network.\n",
+       "    hidden_layer_sizes: List of number of hidden units per hidden layer\n",
+       "    learning_rate: Learning rate of gradient descent\n",
+       "    epochs: Number of passes over the dataset\n",
+       "    """\n",
+       "\n",
+       "    hidden_layer_sizes = hidden_layer_sizes or [3]  # default value\n",
+       "    i_units = len(dataset.inputs)\n",
+       "    o_units = len(dataset.values[dataset.target])\n",
+       "\n",
+       "    # construct a network\n",
+       "    raw_net = network(i_units, hidden_layer_sizes, o_units, activation)\n",
+       "    learned_net = BackPropagationLearner(dataset, raw_net,\n",
+       "                                         learning_rate, epochs, activation)\n",
+       "\n",
+       "    def predict(example):\n",
+       "        # Input nodes\n",
+       "        i_nodes = learned_net[0]\n",
+       "\n",
+       "        # Activate input layer\n",
+       "        for v, n in zip(example, i_nodes):\n",
+       "            n.value = v\n",
+       "\n",
+       "        # Forward pass\n",
+       "        for layer in learned_net[1:]:\n",
+       "            for node in layer:\n",
+       "                inc = [n.value for n in node.inputs]\n",
+       "                in_val = dotproduct(inc, node.weights)\n",
+       "                node.value = node.activation(in_val)\n",
+       "\n",
+       "        # Hypothesis\n",
+       "        o_nodes = learned_net[-1]\n",
+       "        prediction = find_max_node(o_nodes)\n",
+       "        return prediction\n",
+       "\n",
+       "    return predict\n",
+       "
\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "psource(NeuralNetLearner)" ] @@ -169,21 +306,204 @@ "source": [ "### Implementation\n", "\n", - "First, we feed-forward the examples in our neural network. After that, we calculate the gradient for each layers' weights by using the chain rule. Once that is complete, we update all the weights using gradient descent. After running these for a given number of epochs, the function returns the trained Neural Network." + "First, we feed-forward the examples in our neural network. After that, we calculate the gradient for each layers' weights by using the chain rule. Once that is complete, we update all the weights using gradient descent. After running these for a given number of epochs, the function returns the trained Neural Network." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " Codestin Search App\n", + " \n", + " \n", + "\n", + "\n", + "

\n", + "\n", + "
def BackPropagationLearner(dataset, net, learning_rate, epochs, activation=sigmoid):\n",
+       "    """[Figure 18.23] The back-propagation algorithm for multilayer networks"""\n",
+       "    # Initialise weights\n",
+       "    for layer in net:\n",
+       "        for node in layer:\n",
+       "            node.weights = random_weights(min_value=-0.5, max_value=0.5,\n",
+       "                                          num_weights=len(node.weights))\n",
+       "\n",
+       "    examples = dataset.examples\n",
+       "    '''\n",
+       "    As of now dataset.target gives an int instead of list,\n",
+       "    Changing dataset class will have effect on all the learners.\n",
+       "    Will be taken care of later.\n",
+       "    '''\n",
+       "    o_nodes = net[-1]\n",
+       "    i_nodes = net[0]\n",
+       "    o_units = len(o_nodes)\n",
+       "    idx_t = dataset.target\n",
+       "    idx_i = dataset.inputs\n",
+       "    n_layers = len(net)\n",
+       "\n",
+       "    inputs, targets = init_examples(examples, idx_i, idx_t, o_units)\n",
+       "\n",
+       "    for epoch in range(epochs):\n",
+       "        # Iterate over each example\n",
+       "        for e in range(len(examples)):\n",
+       "            i_val = inputs[e]\n",
+       "            t_val = targets[e]\n",
+       "\n",
+       "            # Activate input layer\n",
+       "            for v, n in zip(i_val, i_nodes):\n",
+       "                n.value = v\n",
+       "\n",
+       "            # Forward pass\n",
+       "            for layer in net[1:]:\n",
+       "                for node in layer:\n",
+       "                    inc = [n.value for n in node.inputs]\n",
+       "                    in_val = dotproduct(inc, node.weights)\n",
+       "                    node.value = node.activation(in_val)\n",
+       "\n",
+       "            # Initialize delta\n",
+       "            delta = [[] for _ in range(n_layers)]\n",
+       "\n",
+       "            # Compute outer layer delta\n",
+       "\n",
+       "            # Error for the MSE cost function\n",
+       "            err = [t_val[i] - o_nodes[i].value for i in range(o_units)]\n",
+       "\n",
+       "            # The activation function used is relu or sigmoid function\n",
+       "            if node.activation == sigmoid:\n",
+       "                delta[-1] = [sigmoid_derivative(o_nodes[i].value) * err[i] for i in range(o_units)]\n",
+       "            else:\n",
+       "                delta[-1] = [relu_derivative(o_nodes[i].value) * err[i] for i in range(o_units)]\n",
+       "\n",
+       "            # Backward pass\n",
+       "            h_layers = n_layers - 2\n",
+       "            for i in range(h_layers, 0, -1):\n",
+       "                layer = net[i]\n",
+       "                h_units = len(layer)\n",
+       "                nx_layer = net[i+1]\n",
+       "\n",
+       "                # weights from each ith layer node to each i + 1th layer node\n",
+       "                w = [[node.weights[k] for node in nx_layer] for k in range(h_units)]\n",
+       "\n",
+       "                if activation == sigmoid:\n",
+       "                    delta[i] = [sigmoid_derivative(layer[j].value) * dotproduct(w[j], delta[i+1])\n",
+       "                            for j in range(h_units)]\n",
+       "                else:\n",
+       "                    delta[i] = [relu_derivative(layer[j].value) * dotproduct(w[j], delta[i+1])\n",
+       "                            for j in range(h_units)]\n",
+       "\n",
+       "            #  Update weights\n",
+       "            for i in range(1, n_layers):\n",
+       "                layer = net[i]\n",
+       "                inc = [node.value for node in net[i-1]]\n",
+       "                units = len(layer)\n",
+       "                for j in range(units):\n",
+       "                    layer[j].weights = vector_add(layer[j].weights,\n",
+       "                                                  scalar_vector_product(\n",
+       "                                                  learning_rate * delta[i][j], inc))\n",
+       "\n",
+       "    return net\n",
+       "
\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "psource(BackPropagationLearner)" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -210,6 +530,13 @@ "\n", "To increase accuracy, you can (most of the time) add more layers and nodes. Unfortunately, increasing the number of layers or nodes also increases the computation cost and might result in overfitting." ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -221,14 +548,14 @@ "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.14" + "pygments_lexer": "ipython3", + "version": "3.5.2" } }, "nbformat": 4, diff --git a/utils.py b/utils.py index 1ac0b13f7..5d91c88ef 100644 --- a/utils.py +++ b/utils.py @@ -273,7 +273,15 @@ def sigmoid(x): """Return activation value of x with sigmoid function""" return 1 / (1 + math.exp(-x)) - +def relu(x): + return max(0, x) + +def relu_derivative(value): + if value > 0: + return 1 + else: + return 0 + def step(x): """Return activation value of x with sign function""" return 1 if x >= 0 else 0 From 62f7d67d851382fb7b0ea6e7a61e6dd7c110e9a6 Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Thu, 20 Sep 2018 03:34:21 +0500 Subject: [PATCH 06/15] Changes in texts (#959) Added a few new sentences, modified the sentence structure at a few places, and corrected some grammatical errors. --- agents.ipynb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/agents.ipynb b/agents.ipynb index 02634439d..026dd895e 100644 --- a/agents.ipynb +++ b/agents.ipynb @@ -134,7 +134,7 @@ }, "source": [ "# PROGRAM - BlindDog #\n", - "Now that we have a Park Class, we need to implement a program module for our dog. A program controls how the dog acts upon it's environment. Our program will be very simple, and is shown in the table below.\n", + "Now that we have a Park Class, we need to implement a program module for our dog. A program controls how the dog acts in it's environment; it will be very simple, and it's functionality is illustrated in the table below.\n", "\n", " \n", " \n", @@ -167,13 +167,13 @@ " self.location += 1\n", " \n", " def eat(self, thing):\n", - " '''returns True upon success or False otherwise'''\n", + " '''returns True for success and False otherwise'''\n", " if isinstance(thing, Food):\n", " return True\n", " return False\n", " \n", " def drink(self, thing):\n", - " ''' returns True upon success or False otherwise'''\n", + " ''' returns True for success and False otherwise'''\n", " if isinstance(thing, Water):\n", " return True\n", " return False\n", @@ -289,7 +289,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This is how to implement an agent, its program, and environment. However, this was a very simple case. Let's try a 2-Dimentional environment now with multiple agents.\n", + "This is how to implement an agent, its program, and environment. However, this was a very simple case. Lets now try a 2-Dimensional environment with multiple agents.\n", "\n", "\n", "# 2D Environment #\n", @@ -347,13 +347,13 @@ " self.location[1] += 1\n", " \n", " def eat(self, thing):\n", - " '''returns True upon success or False otherwise'''\n", + " '''returns True for success and False otherwise'''\n", " if isinstance(thing, Food):\n", " return True\n", " return False\n", " \n", " def drink(self, thing):\n", - " ''' returns True upon success or False otherwise'''\n", + " ''' returns True for success and False otherwise'''\n", " if isinstance(thing, Water):\n", " return True\n", " return False\n", @@ -421,11 +421,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This works, but our blind dog doesn't make any use of the 2 dimensional space available to him. Let's make our dog more energetic so that he turns and moves forward, instead of always moving down. We'll also need to make appropriate changes to our environment to be able to handle this extra motion.\n", + "This works, but our blind dog doesn't make any use of the 2 dimensional space available to him. Lets make our dog more energetic so that instead of always moving down, he turns and moves forward as well. To be able to handle this extra motion, we'll need to make appropriate changes to our environment.\n", "\n", "# PROGRAM - EnergeticBlindDog #\n", "\n", - "Let's make our dog turn or move forwards at random - except when he's at the edge of our park - in which case we make him change his direction explicitly by turning to avoid trying to leave the park. Our dog is blind, however, so he wouldn't know which way to turn - he'd just have to try arbitrarily.\n", + "Let's make our dog turn or move forward at random - except when he's at the edge of our park - in which case we make him change his direction explicitly by turning to avoid trying to leave the park. Our dog is blind, however, so he wouldn't know which way to turn - he'd just have to try arbitrarily.\n", "\n", "
Percept:
\n", " \n", @@ -491,13 +491,13 @@ " self.direction = self.direction + d\n", " \n", " def eat(self, thing):\n", - " '''returns True upon success or False otherwise'''\n", + " '''returns True for success and False otherwise'''\n", " if isinstance(thing, Food):\n", " return True\n", " return False\n", " \n", " def drink(self, thing):\n", - " ''' returns True upon success or False otherwise'''\n", + " ''' returns True f success and False otherwise'''\n", " if isinstance(thing, Water):\n", " return True\n", " return False\n", From eee83f6489c5331cb197bb54172de574e6a33454 Mon Sep 17 00:00:00 2001 From: DKE Date: Fri, 28 Sep 2018 05:32:51 +0000 Subject: [PATCH 07/15] Change PriorityQueue expansion (#962) `self.heap.append` simply appends to the end of the `self.heap` Since `self.heap` is just a python list. `self.append` calls the append method of the class instance, effectively putting the item in its proper place. --- utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.py b/utils.py index 5d91c88ef..a514a67eb 100644 --- a/utils.py +++ b/utils.py @@ -717,7 +717,7 @@ def append(self, item): def extend(self, items): """Insert each item in items at its correct position.""" for item in items: - self.heap.append(item) + self.append(item) def pop(self): """Pop and return the item (with min or max f(x) value From 39d2cf6fe6938baac76c1a253bf359594d0057f8 Mon Sep 17 00:00:00 2001 From: Anthony Marakis Date: Mon, 1 Oct 2018 20:49:18 +0100 Subject: [PATCH 08/15] added GSoC 2018 contributors A thank you to contributors from the GSoC 2018 program! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d5dece14c..abb0a8328 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ Here is a table of the implemented data structures, the figure, name of the impl # Acknowledgements -Many thanks for contributions over the years. I got bug reports, corrected code, and other support from Darius Bacon, Phil Ruggera, Peng Shao, Amit Patil, Ted Nienstedt, Jim Martin, Ben Catanzariti, and others. Now that the project is on GitHub, you can see the [contributors](https://github.com/aimacode/aima-python/graphs/contributors) who are doing a great job of actively improving the project. Many thanks to all contributors, especially @darius, @SnShine, @reachtarunhere, @MrDupin, and @Chipe1. +Many thanks for contributions over the years. I got bug reports, corrected code, and other support from Darius Bacon, Phil Ruggera, Peng Shao, Amit Patil, Ted Nienstedt, Jim Martin, Ben Catanzariti, and others. Now that the project is on GitHub, you can see the [contributors](https://github.com/aimacode/aima-python/graphs/contributors) who are doing a great job of actively improving the project. Many thanks to all contributors, especially @darius, @SnShine, @reachtarunhere, @MrDupin, @Chipe1, @ad71 and @MariannaSpyrakou. [agents]:../master/agents.py From 0876fbec742218d303e7d819f18d36f87c0075c8 Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Tue, 2 Oct 2018 01:26:33 +0500 Subject: [PATCH 09/15] Revamped the notebook (#963) * Revamped the notebook * A few changes reversed Changed a few things from my original PR after a review from ad71. --- csp.ipynb | 362 ++++++++++++++++++++++++------------------------------ 1 file changed, 159 insertions(+), 203 deletions(-) diff --git a/csp.ipynb b/csp.ipynb index d9254ef0e..fcf8b5867 100644 --- a/csp.ipynb +++ b/csp.ipynb @@ -12,9 +12,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from csp import *\n", @@ -306,7 +304,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The __ _ _init_ _ __ method parameters specify the CSP. Variable can be passed as a list of strings or integers. Domains are passed as dict where key specify the variables and value specify the domains. The variables are passed as an empty list. Variables are extracted from the keys of the domain dictionary. Neighbor is a dict of variables that essentially describes the constraint graph. Here each variable key has a list its value which are the variables that are constraint along with it. The constraint parameter should be a function **f(A, a, B, b**) that **returns true** if neighbors A, B **satisfy the constraint** when they have values **A=a, B=b**. We have additional parameters like nassings which is incremented each time an assignment is made when calling the assign method. You can read more about the methods and parameters in the class doc string. We will talk more about them as we encounter their use. Let us jump to an example." + "The __ _ _init_ _ __ method parameters specify the CSP. Variables can be passed as a list of strings or integers. Domains are passed as dict (dictionary datatpye) where \"key\" specifies the variables and \"value\" specifies the domains. The variables are passed as an empty list. Variables are extracted from the keys of the domain dictionary. Neighbor is a dict of variables that essentially describes the constraint graph. Here each variable key has a list of its values which are the variables that are constraint along with it. The constraint parameter should be a function **f(A, a, B, b**) that **returns true** if neighbors A, B **satisfy the constraint** when they have values **A=a, B=b**. We have additional parameters like nassings which is incremented each time an assignment is made when calling the assign method. You can read more about the methods and parameters in the class doc string. We will talk more about them as we encounter their use. Let us jump to an example." ] }, { @@ -315,7 +313,7 @@ "source": [ "## GRAPH COLORING\n", "\n", - "We use the graph coloring problem as our running example for demonstrating the different algorithms in the **csp module**. The idea of map coloring problem is that the adjacent nodes (those connected by edges) should not have the same color throughout the graph. The graph can be colored using a fixed number of colors. Here each node is a variable and the values are the colors that can be assigned to them. Given that the domain will be the same for all our nodes we use a custom dict defined by the **UniversalDict** class. The **UniversalDict** Class takes in a parameter which it returns as value for all the keys of the dict. It is very similar to **defaultdict** in Python except that it does not support item assignment." + "We use the graph coloring problem as our running example for demonstrating the different algorithms in the **csp module**. The idea of map coloring problem is that the adjacent nodes (those connected by edges) should not have the same color throughout the graph. The graph can be colored using a fixed number of colors. Here each node is a variable and the values are the colors that can be assigned to them. Given that the domain will be the same for all our nodes we use a custom dict defined by the **UniversalDict** class. The **UniversalDict** Class takes in a parameter and returns it as a value for all the keys of the dict. It is very similar to **defaultdict** in Python except that it does not support item assignment." ] }, { @@ -343,7 +341,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "For our CSP we also need to define a constraint function **f(A, a, B, b)**. In this what we need is that the neighbors must not have the same color. This is defined in the function **different_values_constraint** of the module." + "For our CSP we also need to define a constraint function **f(A, a, B, b)**. In this, we need to ensure that the neighbors don't have the same color. This is defined in the function **different_values_constraint** of the module." ] }, { @@ -463,15 +461,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The CSP class takes neighbors in the form of a Dict. The module specifies a simple helper function named **parse_neighbors** which allows us to take input in the form of strings and return a Dict of a form compatible with the **CSP Class**." + "The CSP class takes neighbors in the form of a Dict. The module specifies a simple helper function named **parse_neighbors** which allows us to take input in the form of strings and return a Dict of a form that is compatible with the **CSP Class**." ] }, { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%pdoc parse_neighbors" @@ -481,7 +477,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The **MapColoringCSP** function creates and returns a CSP with the above constraint function and states. The variables are the keys of the neighbors dict and the constraint is the one specified by the **different_values_constratint** function. **australia**, **usa** and **france** are three CSPs that have been created using **MapColoringCSP**. **australia** corresponds to ** Figure 6.1 ** in the book." + "The **MapColoringCSP** function creates and returns a CSP with the above constraint function and states. The variables are the keys of the neighbors dict and the constraint is the one specified by the **different_values_constratint** function. **Australia**, **USA** and **France** are three CSPs that have been created using **MapColoringCSP**. **Australia** corresponds to ** Figure 6.1 ** in the book." ] }, { @@ -611,9 +607,7 @@ { "data": { "text/plain": [ - "(,\n", - " ,\n", - " )" + "(, , )" ] }, "execution_count": 7, @@ -631,7 +625,7 @@ "source": [ "## N-QUEENS\n", "\n", - "The N-queens puzzle is the problem of placing N chess queens on an N×N chessboard so that no two queens threaten each other. Here N is a natural number. Like the graph coloring problem, NQueens is also implemented in the csp module. The **NQueensCSP** class inherits from the **CSP** class. It makes some modifications in the methods to suit the particular problem. The queens are assumed to be placed one per column, from left to right. That means position (x, y) represents (var, val) in the CSP. The constraint that needs to be passed on the CSP is defined in the **queen_constraint** function. The constraint is satisfied (true) if A, B are really the same variable, or if they are not in the same row, down diagonal, or up diagonal. " + "The N-queens puzzle is the problem of placing N chess queens on an N×N chessboard in a way such that no two queens threaten each other. Here N is a natural number. Like the graph coloring problem, NQueens is also implemented in the csp module. The **NQueensCSP** class inherits from the **CSP** class. It makes some modifications in the methods to suit this particular problem. The queens are assumed to be placed one per column, from left to right. That means position (x, y) represents (var, val) in the CSP. The constraint that needs to be passed to the CSP is defined in the **queen_constraint** function. The constraint is satisfied (true) if A, B are really the same variable, or if they are not in the same row, down diagonal, or up diagonal. " ] }, { @@ -752,7 +746,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The **NQueensCSP** method implements methods that support solving the problem via **min_conflicts** which is one of the techniques for solving CSPs. Because **min_conflicts** hill climbs the number of conflicts to solve, the CSP **assign** and **unassign** are modified to record conflicts. More details about the structures **rows**, **downs**, **ups** which help in recording conflicts are explained in the docstring." + "The **NQueensCSP** method implements methods that support solving the problem via **min_conflicts** which is one of the many popular techniques for solving CSPs. Because **min_conflicts** hill climbs the number of conflicts to solve, the CSP **assign** and **unassign** are modified to record conflicts. More details about the structures: **rows**, **downs**, **ups** which help in recording conflicts are explained in the docstring." ] }, { @@ -950,15 +944,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The _ ___init___ _ method takes only one parameter **n** the size of the problem. To create an instance we just pass the required n into the constructor." + "The _ ___init___ _ method takes only one parameter **n** i.e. the size of the problem. To create an instance, we just pass the required value of n into the constructor." ] }, { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "eight_queens = NQueensCSP(8)" @@ -969,18 +961,18 @@ "metadata": {}, "source": [ "We have defined our CSP. \n", - "We now need to solve this.\n", + "Now, we need to solve this.\n", "\n", "### Min-conflicts\n", "As stated above, the `min_conflicts` algorithm is an efficient method to solve such a problem.\n", "
\n", - "To begin with, all the variables of the CSP are _randomly_ initialized. \n", + "In the start, all the variables of the CSP are _randomly_ initialized. \n", "
\n", "The algorithm then randomly selects a variable that has conflicts and violates some constraints of the CSP.\n", "
\n", "The selected variable is then assigned a value that _minimizes_ the number of conflicts.\n", "
\n", - "This is a simple stochastic algorithm which works on a principle similar to **Hill-climbing**.\n", + "This is a simple **stochastic algorithm** which works on a principle similar to **Hill-climbing**.\n", "The conflicting state is repeatedly changed into a state with fewer conflicts in an attempt to reach an approximate solution.\n", "
\n", "This algorithm sometimes benefits from having a good initial assignment.\n", @@ -1123,9 +1115,7 @@ { "cell_type": "code", "execution_count": 12, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "solution = min_conflicts(eight_queens)" @@ -1147,9 +1137,9 @@ "outputs": [ { "data": { - "image/png": "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\nYrfnP4SxJKkAAAAASUVORK5CYII=\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, "metadata": {}, @@ -1174,9 +1164,9 @@ "outputs": [ { "data": { - "image/png": "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\nAUlr8AXRtSNBAAAAAElFTkSuQmCC\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, "metadata": {}, @@ -1194,9 +1184,9 @@ "metadata": {}, "source": [ "The solution is a bit different this time. \n", - "Running the above cell several times should give you various valid solutions.\n", + "Running the above cell several times should give you different valid solutions.\n", "
\n", - "In the `search.ipynb` notebook, we will see how NQueensProblem can be solved using a heuristic search method such as `uniform_cost_search` and `astar_search`." + "In the `search.ipynb` notebook, we will see how NQueensProblem can be solved using a **heuristic search method** such as `uniform_cost_search` and `astar_search`." ] }, { @@ -1205,15 +1195,13 @@ "source": [ "### Helper Functions\n", "\n", - "We will now implement a few helper functions that will help us visualize the Coloring Problem. We will make some modifications to the existing Classes and Functions for additional book keeping. To begin we modify the **assign** and **unassign** methods in the **CSP** to add a copy of the assignment to the **assignment_history**. We call this new class **InstruCSP**. This will allow us to see how the assignment evolves over time." + "We will now implement a few helper functions that will allow us to visualize the Coloring Problem; we'll also make a few modifications to the existing classes and functions for additional record keeping. To begin, we modify the **assign** and **unassign** methods in the **CSP** in order to add a copy of the assignment to the **assignment_history**. We name this new class as **InstruCSP**; it will allow us to see how the assignment evolves over time. " ] }, { "cell_type": "code", "execution_count": 15, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "import copy\n", @@ -1236,15 +1224,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Next, we define **make_instru** which takes an instance of **CSP** and returns a **InstruCSP** instance. " + "Next, we define **make_instru** which takes an instance of **CSP** and returns an instance of **InstruCSP**." ] }, { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def make_instru(csp):\n", @@ -1255,15 +1241,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will now use a graph defined as a dictionary for plotting purposes in our Graph Coloring Problem. The keys are the nodes and their corresponding values are the nodes they are connected to." + "We will now use a graph defined as a dictionary for plotting purposes in our Graph Coloring Problem. The keys are the nodes and their values are the corresponding nodes they are connected to." ] }, { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "neighbors = {\n", @@ -1301,9 +1285,7 @@ { "cell_type": "code", "execution_count": 18, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "coloring_problem = MapColoringCSP('RGBY', neighbors)" @@ -1312,9 +1294,7 @@ { "cell_type": "code", "execution_count": 19, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "coloring_problem1 = make_instru(coloring_problem)" @@ -1325,14 +1305,14 @@ "metadata": {}, "source": [ "### CONSTRAINT PROPAGATION\n", - "Algorithms that solve CSPs have a choice between searching and or do a _constraint propagation_, a specific type of inference.\n", - "The constraints can be used to reduce the number of legal values for a another variable, which in turn can reduce the legal values for another variable, and so on.\n", + "Algorithms that solve CSPs have a choice between searching and or doing a _constraint propagation_, a specific type of inference.\n", + "The constraints can be used to reduce the number of legal values for another variable, which in turn can reduce the legal values for some other variable, and so on. \n", "
\n", "Constraint propagation tries to enforce _local consistency_.\n", "Consider each variable as a node in a graph and each binary constraint as an arc.\n", "Enforcing local consistency causes inconsistent values to be eliminated throughout the graph, \n", "a lot like the `GraphPlan` algorithm in planning, where mutex links are removed from a planning graph.\n", - "There are different types of local consistency:\n", + "There are different types of local consistencies:\n", "1. Node consistency\n", "2. Arc consistency\n", "3. Path consistency\n", @@ -1638,9 +1618,7 @@ { "cell_type": "code", "execution_count": 22, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "neighbors = parse_neighbors('A: B; B: ')\n", @@ -1659,9 +1637,7 @@ { "cell_type": "code", "execution_count": 23, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "csp = CSP(variables=None, domains=domains, neighbors=neighbors, constraints=constraints)" @@ -1697,9 +1673,7 @@ { "cell_type": "code", "execution_count": 25, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "constraints = lambda X, x, Y, y: (x % 2) == 0 and (x + y) == 4\n", @@ -1740,15 +1714,13 @@ "source": [ "## BACKTRACKING SEARCH\n", "\n", - "For solving a CSP the main issue with Naive search algorithms is that they can continue expanding obviously wrong paths. In backtracking search, we check constraints as we go. Backtracking is just the above idea combined with the fact that we are dealing with one variable at a time. Backtracking Search is implemented in the repository as the function **backtracking_search**. This is the same as **Figure 6.5** in the book. The function takes as input a CSP and few other optional parameters which can be used to further speed it up. The function returns the correct assignment if it satisfies the goal. We will discuss these later. Let us solve our **coloring_problem1** with **backtracking_search**." + "The main issue with using Naive Search Algorithms to solve a CSP is that they can continue to expand obviously wrong paths; whereas, in **backtracking search**, we check the constraints as we go and we deal with only one variable at a time. Backtracking Search is implemented in the repository as the function **backtracking_search**. This is the same as **Figure 6.5** in the book. The function takes as input a CSP and a few other optional parameters which can be used to speed it up further. The function returns the correct assignment if it satisfies the goal. However, we will discuss these later. For now, let us solve our **coloring_problem1** with **backtracking_search**." ] }, { "cell_type": "code", "execution_count": 27, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "result = backtracking_search(coloring_problem1)" @@ -1825,7 +1797,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now let us check the total number of assignments and unassignments which is the length of our assignment history." + "Now, let us check the total number of assignments and unassignments, which would be the length of our assignment history. We can see it by using the command below. " ] }, { @@ -1852,9 +1824,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now let us explore the optional keyword arguments that the **backtracking_search** function takes. These optional arguments help speed up the assignment further. Along with these, we will also point out to methods in the CSP class that help make this work. \n", + "Now let us explore the optional keyword arguments that the **backtracking_search** function takes. These optional arguments help speed up the assignment further. Along with these, we will also point out the methods in the CSP class that help to make this work. \n", "\n", - "The first of these is **select_unassigned_variable**. It takes in a function that helps in deciding the order in which variables will be selected for assignment. We use a heuristic called Most Restricted Variable which is implemented by the function **mrv**. The idea behind **mrv** is to choose the variable with the fewest legal values left in its domain. The intuition behind selecting the **mrv** or the most constrained variable is that it allows us to encounter failure quickly before going too deep into a tree if we have selected a wrong step before. The **mrv** implementation makes use of another function **num_legal_values** to sort out the variables by a number of legal values left in its domain. This function, in turn, calls the **nconflicts** method of the **CSP** to return such values.\n" + "The first one is **select_unassigned_variable**. It takes in, as a parameter, a function that helps in deciding the order in which the variables will be selected for assignment. We use a heuristic called Most Restricted Variable which is implemented by the function **mrv**. The idea behind **mrv** is to choose the variable with the least legal values left in its domain. The intuition behind selecting the **mrv** or the most constrained variable is that it allows us to encounter failure quickly before going too deep into a tree if we have selected a wrong step before. The **mrv** implementation makes use of another function **num_legal_values** to sort out the variables by the number of legal values left in its domain. This function, in turn, calls the **nconflicts** method of the **CSP** to return such values." ] }, { @@ -2209,7 +2181,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Another ordering related parameter **order_domain_values** governs the value ordering. Here we select the Least Constraining Value which is implemented by the function **lcv**. The idea is to select the value which rules out the fewest values in the remaining variables. The intuition behind selecting the **lcv** is that it leaves a lot of freedom to assign values later. The idea behind selecting the mrc and lcv makes sense because we need to do all variables but for values, we might better try the ones that are likely. So for vars, we face the hard ones first.\n" + "Another ordering related parameter **order_domain_values** governs the value ordering. Here we select the Least Constraining Value which is implemented by the function **lcv**. The idea is to select the value which rules out least number of values in the remaining variables. The intuition behind selecting the **lcv** is that it allows a lot of freedom to assign values later. The idea behind selecting the mrc and lcv makes sense because we need to do all variables but for values, and it's better to try the ones that are likely. So for vars, we face the hard ones first." ] }, { @@ -2330,22 +2302,20 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, the third parameter **inference** can make use of one of the two techniques called Arc Consistency or Forward Checking. The details of these methods can be found in the **Section 6.3.2** of the book. In short the idea of inference is to detect the possible failure before it occurs and to look ahead to not make mistakes. **mac** and **forward_checking** implement these two techniques. The **CSP** methods **support_pruning**, **suppose**, **prune**, **choices**, **infer_assignment** and **restore** help in using these techniques. You can know more about these by looking up the source code." + "Finally, the third parameter **inference** can make use of one of the two techniques called Arc Consistency or Forward Checking. The details of these methods can be found in the **Section 6.3.2** of the book. In short the idea of inference is to detect the possible failure before it occurs and to look ahead to not make mistakes. **mac** and **forward_checking** implement these two techniques. The **CSP** methods **support_pruning**, **suppose**, **prune**, **choices**, **infer_assignment** and **restore** help in using these techniques. You can find out more about these by looking up the source code." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now let us compare the performance with these parameters enabled vs the default parameters. We will use the Graph Coloring problem instance usa for comparison. We will call the instances **solve_simple** and **solve_parameters** and solve them using backtracking and compare the number of assignments." + "Now let us compare the performance with these parameters enabled vs the default parameters. We will use the Graph Coloring problem instance 'usa' for comparison. We will call the instances **solve_simple** and **solve_parameters** and solve them using backtracking and compare the number of assignments." ] }, { "cell_type": "code", "execution_count": 35, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "solve_simple = copy.deepcopy(usa)\n", @@ -2360,55 +2330,55 @@ { "data": { "text/plain": [ - "{'AL': 'G',\n", - " 'AR': 'G',\n", - " 'AZ': 'B',\n", - " 'CA': 'Y',\n", - " 'CO': 'B',\n", - " 'CT': 'R',\n", - " 'DC': 'G',\n", - " 'DE': 'B',\n", - " 'FL': 'R',\n", - " 'GA': 'B',\n", - " 'IA': 'G',\n", - " 'ID': 'B',\n", - " 'IL': 'R',\n", - " 'IN': 'B',\n", - " 'KA': 'G',\n", - " 'KY': 'G',\n", - " 'LA': 'R',\n", - " 'MA': 'G',\n", + "{'NJ': 'R',\n", + " 'DE': 'G',\n", + " 'PA': 'B',\n", " 'MD': 'R',\n", - " 'ME': 'R',\n", - " 'MI': 'G',\n", - " 'MN': 'R',\n", - " 'MO': 'B',\n", - " 'MS': 'B',\n", - " 'MT': 'R',\n", - " 'NC': 'G',\n", - " 'ND': 'G',\n", - " 'NE': 'R',\n", - " 'NH': 'B',\n", - " 'NJ': 'R',\n", - " 'NM': 'G',\n", - " 'NV': 'G',\n", - " 'NY': 'B',\n", + " 'NY': 'G',\n", + " 'WV': 'G',\n", + " 'VA': 'B',\n", " 'OH': 'R',\n", + " 'KY': 'Y',\n", + " 'IN': 'G',\n", + " 'IL': 'R',\n", + " 'MO': 'G',\n", + " 'TN': 'R',\n", + " 'AR': 'B',\n", " 'OK': 'R',\n", - " 'OR': 'R',\n", - " 'PA': 'G',\n", - " 'RI': 'B',\n", + " 'IA': 'B',\n", + " 'NE': 'R',\n", + " 'MI': 'B',\n", + " 'TX': 'G',\n", + " 'NM': 'B',\n", + " 'LA': 'R',\n", + " 'KA': 'B',\n", + " 'NC': 'G',\n", + " 'GA': 'B',\n", + " 'MS': 'G',\n", + " 'AL': 'Y',\n", + " 'CO': 'G',\n", + " 'WY': 'B',\n", " 'SC': 'R',\n", - " 'SD': 'B',\n", - " 'TN': 'R',\n", - " 'TX': 'B',\n", + " 'FL': 'R',\n", " 'UT': 'R',\n", - " 'VA': 'B',\n", + " 'ID': 'G',\n", + " 'SD': 'G',\n", + " 'MT': 'R',\n", + " 'ND': 'B',\n", + " 'DC': 'G',\n", + " 'NV': 'B',\n", + " 'OR': 'R',\n", + " 'MN': 'R',\n", + " 'CA': 'G',\n", + " 'AZ': 'Y',\n", + " 'WA': 'B',\n", + " 'WI': 'G',\n", + " 'CT': 'R',\n", + " 'MA': 'B',\n", " 'VT': 'R',\n", - " 'WA': 'G',\n", - " 'WI': 'B',\n", - " 'WV': 'Y',\n", - " 'WY': 'G'}" + " 'NH': 'G',\n", + " 'RI': 'G',\n", + " 'ME': 'R'}" ] }, "execution_count": 36, @@ -2469,15 +2439,15 @@ "\n", "The `tree_csp_solver` function (**Figure 6.11** in the book) can be used to solve problems whose constraint graph is a tree. Given a CSP, with `neighbors` forming a tree, it returns an assignment that satisfies the given constraints. The algorithm works as follows:\n", "\n", - "First it finds the *topological sort* of the tree. This is an ordering of the tree where each variable/node comes after its parent in the tree. The function that accomplishes this is `topological_sort`, which builds the topological sort using the recursive function `build_topological`. That function is an augmented DFS, where each newly visited node of the tree is pushed on a stack. The stack in the end holds the variables topologically sorted.\n", + "First it finds the *topological sort* of the tree. This is an ordering of the tree where each variable/node comes after its parent in the tree. The function that accomplishes this is `topological_sort`; it builds the topological sort using the recursive function `build_topological`. That function is an augmented DFS (Depth First Search), where each newly visited node of the tree is pushed on a stack. The stack in the end holds the variables topologically sorted.\n", "\n", - "Then the algorithm makes arcs between each parent and child consistent. *Arc-consistency* between two variables, *a* and *b*, occurs when for every possible value of *a* there is an assignment in *b* that satisfies the problem's constraints. If such an assignment cannot be found, then the problematic value is removed from *a*'s possible values. This is done with the use of the function `make_arc_consistent` which takes as arguments a variable `Xj` and its parent, and makes the arc between them consistent by removing any values from the parent which do not allow for a consistent assignment in `Xj`.\n", + "Then the algorithm makes arcs between each parent and child consistent. *Arc-consistency* between two variables, *a* and *b*, occurs when for every possible value of *a* there is an assignment in *b* that satisfies the problem's constraints. If such an assignment cannot be found, the problematic value is removed from *a*'s possible values. This is done with the use of the function `make_arc_consistent`, which takes as arguments a variable `Xj` and its parent, and makes the arc between them consistent by removing any values from the parent which do not allow for a consistent assignment in `Xj`.\n", "\n", "If an arc cannot be made consistent, the solver fails. If every arc is made consistent, we move to assigning values.\n", "\n", - "First we assign a random value to the root from its domain and then we start assigning values to the rest of the variables. Since the graph is now arc-consistent, we can simply move from variable to variable picking any remaining consistent values. At the end we are left with a valid assignment. If at any point though we find a variable where no consistent value is left in its domain, the solver fails.\n", + "First we assign a random value to the root from its domain and then we assign values to the rest of the variables. Since the graph is now arc-consistent, we can simply move from variable to variable picking any remaining consistent values. At the end we are left with a valid assignment. If at any point though we find a variable where no consistent value is left in its domain, the solver fails.\n", "\n", - "The implementation of the algorithm:" + "Run the cell below to see the implementation of the algorithm:" ] }, { @@ -2611,19 +2581,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will now use the above function to solve a problem. More specifically, we will solve the problem of coloring the map of Australia. At our disposal we have two colors: Red and Blue. As a reminder, this is the graph of Australia:\n", + "We will now use the above function to solve a problem. More specifically, we will solve the problem of coloring Australia's map. We have two colors at our disposal: Red and Blue. As a reminder, this is the graph of Australia:\n", "\n", "`\"SA: WA NT Q NSW V; NT: WA Q; NSW: Q V; T: \"`\n", "\n", - "Unfortunately as you can see the above is not a tree. If, though, we remove `SA`, which has arcs to `WA`, `NT`, `Q`, `NSW` and `V`, we are left with a tree (we also remove `T`, since it has no in-or-out arcs). We can now solve this using our algorithm. Let's define the map coloring problem at hand:" + "Unfortunately, as you can see, the above is not a tree. However, if we remove `SA`, which has arcs to `WA`, `NT`, `Q`, `NSW` and `V`, we are left with a tree (we also remove `T`, since it has no in-or-out arcs). We can now solve this using our algorithm. Let's define the map coloring problem at hand:" ] }, { "cell_type": "code", "execution_count": 40, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "australia_small = MapColoringCSP(list('RB'),\n", @@ -2634,7 +2602,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will input `australia_small` to the `tree_csp_solver` and we will print the given assignment." + "We will input `australia_small` to the `tree_csp_solver` and print the given assignment." ] }, { @@ -2668,15 +2636,13 @@ "source": [ "## GRAPH COLORING VISUALIZATION\n", "\n", - "Next, we define some functions to create the visualisation from the assignment_history of **coloring_problem1**. The reader need not concern himself with the code that immediately follows as it is the usage of Matplotib with IPython Widgets. If you are interested in reading more about these visit [ipywidgets.readthedocs.io](http://ipywidgets.readthedocs.io). We will be using the **networkx** library to generate graphs. These graphs can be treated as the graph that needs to be colored or as a constraint graph for this problem. If interested you can read a dead simple tutorial [here](https://www.udacity.com/wiki/creating-network-graphs-with-python). We start by importing the necessary libraries and initializing matplotlib inline.\n" + "Next, we define some functions to create the visualisation from the assignment_history of **coloring_problem1**. The readers need not concern themselves with the code that immediately follows as it is the usage of Matplotib with IPython Widgets. If you are interested in reading more about these, visit [ipywidgets.readthedocs.io](http://ipywidgets.readthedocs.io). We will be using the **networkx** library to generate graphs. These graphs can be treated as graphs that need to be colored or as constraint graphs for this problem. If interested you can check out a fairly simple tutorial [here](https://www.udacity.com/wiki/creating-network-graphs-with-python). We start by importing the necessary libraries and initializing matplotlib inline.\n" ] }, { "cell_type": "code", "execution_count": 42, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -2690,23 +2656,21 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The ipython widgets we will be using require the plots in the form of a step function such that there is a graph corresponding to each value. We define the **make_update_step_function** which return such a function. It takes in as inputs the neighbors/graph along with an instance of the **InstruCSP**. This will be more clear with the example below. If this sounds confusing do not worry this is not the part of the core material and our only goal is to help you visualize how the process works." + "The ipython widgets we will be using require the plots in the form of a step function such that there is a graph corresponding to each value. We define the **make_update_step_function** which returns such a function. It takes in as inputs the neighbors/graph along with an instance of the **InstruCSP**. The example below will elaborate it further. If this sounds confusing, don't worry. This is not part of the core material and our only goal is to help you visualize how the process works." ] }, { "cell_type": "code", "execution_count": 43, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def make_update_step_function(graph, instru_csp):\n", " \n", + " #define a function to draw the graphs\n", " def draw_graph(graph):\n", - " # create networkx graph\n", + " \n", " G=nx.Graph(graph)\n", - " # draw graph\n", " pos = nx.spring_layout(G,k=0.15)\n", " return (G, pos)\n", " \n", @@ -2725,11 +2689,11 @@ " nx.draw(G, pos, node_color=colors, node_size=500)\n", "\n", " labels = {label:label for label in G.node}\n", - " # Labels shifted by offset so as to not overlap nodes.\n", + " # Labels shifted by offset so that nodes don't overlap\n", " label_pos = {key:[value[0], value[1]+0.03] for key, value in pos.items()}\n", " nx.draw_networkx_labels(G, label_pos, labels, font_size=20)\n", "\n", - " # show graph\n", + " # display the graph\n", " plt.show()\n", "\n", " return update_step # <-- this is a function\n", @@ -2753,15 +2717,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally let us plot our problem. We first use the function above to obtain a step function." + "Finally let us plot our problem. We first use the function below to obtain a step function." ] }, { "cell_type": "code", "execution_count": 44, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "step_func = make_update_step_function(neighbors, coloring_problem1)" @@ -2771,15 +2733,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Next we set the canvas size." + "Next, we set the canvas size." ] }, { "cell_type": "code", "execution_count": 45, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "matplotlib.rcParams['figure.figsize'] = (18.0, 18.0)" @@ -2789,7 +2749,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally our plot using ipywidget slider and matplotib. You can move the slider to experiment and see the coloring change. It is also possible to move the slider using arrow keys or to jump to the value by directly editing the number with a double click. The **Visualize Button** will automatically animate the slider for you. The **Extra Delay Box** allows you to set time delay in seconds upto one second for each time step." + "Finally, our plot using ipywidget slider and matplotib. You can move the slider to experiment and see the colors change. It is also possible to move the slider using arrow keys or to jump to the value by directly editing the number with a double click. The **Visualize Button** will automatically animate the slider for you. The **Extra Delay Box** allows you to set time delay in seconds (upto one second) for each time step." ] }, { @@ -2799,26 +2759,28 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABTgAAAUyCAYAAAAqcpudAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4wLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvpW3flQAAIABJREFUeJzs3U3IpWUZwPHrjDOj8wFlBaUoaYgE\nzoAWhNAQqBOtdFVEFCVRVAs3ESSCtMlVQXtHSSFw0SJUBPEDXQlKBGkGA9GEBSEmLWyc8R3ltFDH\n+Xg/zsdzP8993ffvt3zf51xc6z/Xc85sPp/PAwAAAAAgoV1TLwAAAAAAsCqBEwAAAABIS+AEAAAA\nANISOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL\n4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMA\nAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAA\nIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQE\nTgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEA\nAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA\n0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvg\nBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAA\nAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAg\nLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtARO\nAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAACgKffdd1/M\nZrOYzWZx/PjxqdehMIETAAAAgGbM5/N48MEHYzabRUTEsWPHJt6I0gROAAAAAJrx1FNPxYkTJ+J7\n3/tefPrTn46HH344NjY2pl6LggROAAAAAJrx4cXmD3/4w/j2t78d//nPf+IPf/jDxFtR0mw+n8+n\nXgIAAAAA1vX666/H1VdfHddee20cP348/vKXv8Thw4fj1ltvjWeffXbq9SjEBScAAAAATfjtb38b\nZ86ciTvvvDMiIg4dOhRf+MIX4rnnnou//e1v0y5HMQInAAAAAOnN5/N44IEHYteuXfHd73737N/v\nvPPOs/+jTV5RBwAAACC9Z599No4ePRpf+9rX4sknnzz79zfffDOuvPLKuPzyy+Of//xn7NmzZ8It\nKcEFJwAAAADp3X///RERZ19P/9AnP/nJuP322+P111+PRx99dILNKM0FJwAAAACpvfHGG3HVVVfF\nxsbGts999atfjaeeemqkrRjL7qkXAAAAAIB1PPzww7GxsRFf/OIX48Ybb9z0mcceeyyeeeaZOHHi\nRFx77bUjb0hJLjgBAAAASO3zn/98HD9+PF588cX40pe+tOkz9957b/zyl7+Me+65J+67776RN6Qk\ngRMAAACAtJ5//vm45ZZb4vDhw/Hyyy9v+dw//vGP+NznPhef+cxn4rXXXovdu73Y3Ao/MgQAAABA\nWseOHYuIiB/84AfbPnfNNdfE0aNH49///nc8/vjjY6zGSFxwAgAAAABpueAEAAAAANISOAEAAACA\ntAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4\nAQAAAIC0BE4AAAAAIC2BEwAAAABIa/fUCwAAAADAEDY2NuLVV1+NV155JU6ePBl79+6N66+/Pm66\n6aY4ePDg1OtRiMAJAAAAQGovvPBC/PrXv44nnngiLr300pjP5/Hee+/Frl274pJLLolTp07FTTfd\nFD//+c/jjjvuiN27JbGWzObz+XzqJQAAAABgWW+88UZ8//vfj+eeey7efvvt2ClzHTx4MK6++ur4\n/e9/HzfccMNIW1KawAkAAABAOn/605/itttui7fffjs2NjYW/txsNovLLrss7r///vjOd75TcEPG\nInACAAAAkMrLL78cR44cibfeemvlGfv27YsHH3wwvvWtbw24GVMQOAEAAABI49SpU3H99dfHv/71\n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\ngEGImwBMQeAEAABgbeImAFMROAEAAFiLuAnAlAROAAAAViZuAjA1gRMAAICViJsA1EDgBAAAYGni\nJgC1EDgBAABYirgJQE0ETgAAABYmbgJQG4ETAACAhYibANRI4AQAAGBH4iYAtRI4AQAA2Ja4CUDN\nBE4AAAC2JG4CUDuBEwAAgE2JmwBkIHACAABwEXETgCwETgAAAM4jbgKQicAJAADAWeImANkInAAA\nAESEuAlATgInAAAA4iYAaQmcAAAAnRM3AchM4AQAAOiYuAlAdgInAABAp8RNAFogcAIAAHRI3ASg\nFQInAABAZ8RNAFoicAIAAHRE3ASgNQInAABAJ8RNAFokcAIAAHRA3ASgVQInAABA48RNAFomcAIA\nADRM3ASgdQInAABAo8RNAHogcAIAADRI3ASgFwInAABAY8RNAHoicAIAADRE3ASgNwInAABAI8RN\nAHokcAIAADRA3ASgVwInAABAcuImAD0TOAEAABITNwHoncAJAACQlLgJAAInAABASuImALxP4AQA\nAEhG3ASAjwicAAAAiYibAHA+gRMAACAJcRMALiZwAgAAJCBuAsDmBE4AAIDKiZsAsDWBEwAAoGLi\nJgBsT+AEAAColLgJADsTOAEAACokbgLAYgROAACAyoibALA4gRMAAKAi4iYALEfgBAAAqIS4CQDL\nEzgBAAAqIG4CwGoETgAAgImJmwCwOoETAABgQuImAKxH4AQAAJiIuAkA6xM4AQAAJiBuAsAwBE4A\nAICRiZsAMByBEwAAYETiJgAMS+AEAAAYibgJAMMTOAEAAEYgbgJAGQInAABAYeImAJQjcAIAABQk\nbgJAWQInAABAIeImAJQncAIAABQgbgLAOAROAACAgYmbADAegRMAAGBA4iYAjEvgBAAAGIi4CQDj\nEzgBAAAGIG4CwDQETgAAgDWJmwAwHYETAABgDeImAExL4AQAAFiRuAkA0xM4AQAAViBuAkAdBE4A\nAIAliZsAUA+BEwAAYAniJgDUReAEAABYkLgJAPUROAEAABYgbgJAnQROAACAHYibAFAvgRMAAGAb\n4iYA1E3gBAAA2IK4CQD1EzgBAAA2IW4CQA4CJwAAwAXETQDIQ+AEAAA4h7gJALkInAAAAB8QNwEg\nH4ETAAAgxE0AyErgBAAAuiduAkBeAicAANA1cRMAchM4AQCAbombAJCfwAkAAHRJ3ASANgicAABA\n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\ntErgBIDkxE0AAKBlAicAJCZuAgAArRM4ASApcRMAAEDgBICUxE0AAIADBE4ASEbcBAAA+I/ACQCJ\niJsAAABHEjgBIAlxEwAAYC2BEwASEDcBAADWJ3ACQOHETQAAgI0JnABQMHETAABgcwInABRK3AQA\nANiawAkABRI3AQAAJiNwAkBhxE0AAIDJCZwAUBBxEwAAYDoCJwAUQtwEAACYnsAJAAUQNwEAAGYj\ncALAyMRNAACA2QmcADAicRMAAGA+AicAjETcBAAAmJ/ACQAjEDcBAAC6IXACwMDETQAAgO4InAAw\nIHETAACgWwInAAxE3AQAAOiewAkAAxA3AQAA+iFwAkDPxE0AAID+CJwA0CNxEwAAoF8CJwD0RNwE\nAADon8AJAD0QNwEAAIYhcAJAx8RNAACA4QicANAhcRMAAGBYAicAdETcBAAAGJ7ACQAdEDcBAADG\nIXACwJzETQAAgPEInAAwB3ETAABgXAInAMxI3AQAABifwAkAMxA3AQAAyiBwAsCUxE0AAIByCJwA\nMAVxEwAAoCwCJwBMSNwEAAAoj8AJABMQNwEAAMokcALAFsRNAACAcgmcALAJcRMAAKBsAicAbEDc\nBAAAKJ/ACQDrEDcBAAByEDgB4CjiJgAAQB4CJwAcRtwEAADIReAEgH+JmwAAAPkInAAQ4iYAAEBW\nAicAzRM3AQAA8hI4AWiauAkAAJCbwAlAs8RNAACA/AROAJokbgIAANRB4ASgOeImAABAPQROAJoi\nbgIAANRF4ASgGeImAABAfQROAJogbgIAANRJ4ASgeuImAABAvQROAKombgIAANRN4ASgWuImAABA\n/QROAKokbgIAALRB4ASgOuImAABAOwROAKoibgIAALRF4ASgGuImAABAewROAKogbgIAALRJ4AQg\nPXETAACgXQInAKmJmwAAAG0TOAFIS9wEAABA4AQgJXETAACACIETgITETQAAAA4SOAFIRdwEAADg\ncAInAGmImwAAABxN4AQgBXETAACA9QicABRP3AQAAGAjAicARRM3AQAA2IzACUCxxE0AAAC2InAC\nUCRxEwAAgEkInAAUR9wEAABgUgInAEURNwEAAJiGwAlAMcRNAAAApiVwAlAEcRMAAIBZCJwAjE7c\nBAAAYFYCJwCjEjcBAACYh8AJwGjETQAAAOYlcAIwCnETAACALgicAAxO3AQAAKArAicAgxI3AQAA\n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\n/Pbbb53PXlpaij/++COOPfbYzmcDAAAA0DYnOImIA7eRX3rppb3MPv/888VNAAAAAHohcHLIPffc\n0/mt5MvLy3H33Xd3OhMAAAAADnKLOofs3bs3Tj311Ni9e3dnM48//vj47rvvYufOnZ3NBAAAAICD\nnODkkG3btsUzzzwTy8vLncxbXl6Oxx9/XNwEAAAAoDdOcHKE1dXVuOGGG+L111+Pv/76a+Y527Zt\ni0suuSTeeeedWFzU0QEAAADoh8DJGnv27Imrr746Pv/885ki57Zt2+Lss8+O999/P0466aQeNgQA\nAACAAxytY43l5eV444034oYbbogTTjhh6s9ec8014iYAAAAAg3CCk0299NJLce+998avv/4av//+\n+4bv27FjRxx33HHx5JNPxq233hoLCwsDbgkAAABAqwROtrR///547bXXYteuXfHuu+/G119/HSsr\nK7G4uBhnnXVWXHHFFXHbbbfF9ddfH8ccc8zY6wIAAADQEIGTmaysrIiZAAAAAIxO4AQAAAAA0vKQ\nIQAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEA\nAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA\n0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvg\nBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAA\nAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAg\nLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtARO\nAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAA\nAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADS\nEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AE\nAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAA\nAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAt\ngRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4A\nAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAA\ngLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANIS\nOAEAAACAtAROAAAAACAtgRMAAAAASEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQA\nAAAA0hI4AQAAAIC0BE4AAAAAIC2BEwAAAABIS+AEAAAAANISOAEAAACAtAROAAAAACAtgRMAAAAA\nSEvgBAAAAADSEjgBAAAAgLQETgAAAAAgLYETAAAAAEhL4AQAAAAA0vo/E5n8oUH60/sAAAAASUVO\nRK5CYII=\n", + "application/vnd.jupyter.widget-view+json": { + "model_id": "26b425b8fade4789a075632715b1afcd", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ - "" + "interactive(children=(IntSlider(value=0, description='iteration', max=20), Output()), _dom_classes=('widget-in…" ] }, "metadata": {}, "output_type": "display_data" }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "The installed widget Javascript is the wrong version. It must satisfy the semver range ~2.1.4.\n" - ] - }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0a993a2fa8864b4d984f41784f8e1e8f" - } + "model_id": "179048eb3f8e41a1afc1ec22343dece4", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(ToggleButton(value=False, description='Visualize'), ToggleButtons(description='Extra Del…" + ] }, "metadata": {}, "output_type": "display_data" @@ -2847,15 +2809,13 @@ "source": [ "## N-QUEENS VISUALIZATION\n", "\n", - "Just like the Graph Coloring Problem we will start with defining a few helper functions to help us visualize the assignments as they evolve over time. The **make_plot_board_step_function** behaves similar to the **make_update_step_function** introduced earlier. It initializes a chess board in the form of a 2D grid with alternating 0s and 1s. This is used by **plot_board_step** function which draws the board using matplotlib and adds queens to it. This function also calls the **label_queen_conflicts** which modifies the grid placing 3 in positions in a position where there is a conflict." + "Just like the Graph Coloring Problem, we will start with defining a few helper functions to help us visualize the assignments as they evolve over time. The **make_plot_board_step_function** behaves similar to the **make_update_step_function** introduced earlier. It initializes a chess board in the form of a 2D grid with alternating 0s and 1s. This is used by **plot_board_step** function which draws the board using matplotlib and adds queens to it. This function also calls the **label_queen_conflicts** which modifies the grid placing a 3 in any position where there is a conflict." ] }, { "cell_type": "code", "execution_count": 47, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "def label_queen_conflicts(assignment,grid):\n", @@ -2868,7 +2828,7 @@ " down_conflicts = {temp_col:temp_row for temp_col,temp_row in assignment.items() \n", " if temp_row-temp_col == row-col and temp_col != col}\n", " \n", - " # Now marking the grid.\n", + " # Place a 3 in positions where this is a conflict\n", " for col, row in row_conflicts.items():\n", " grid[col][row] = 3\n", " for col, row in up_conflicts.items():\n", @@ -2917,15 +2877,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now let us visualize a solution obtained via backtracking. We use of the previosuly defined **make_instru** function for keeping a history of steps." + "Now let us visualize a solution obtained via backtracking. We make use of the previosuly defined **make_instru** function for keeping a history of steps." ] }, { "cell_type": "code", "execution_count": 48, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "twelve_queens_csp = NQueensCSP(12)\n", @@ -2936,9 +2894,7 @@ { "cell_type": "code", "execution_count": 49, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "backtrack_queen_step = make_plot_board_step_function(backtracking_instru_queen) # Step Function for Widgets" @@ -2948,7 +2904,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now finally we set some matplotlib parameters to adjust how our plot will look. The font is necessary because the Black Queen Unicode character is not a part of all fonts. You can move the slider to experiment and observe the how queens are assigned. It is also possible to move the slider using arrow keys or to jump to the value by directly editing the number with a double click.The **Visualize Button** will automatically animate the slider for you. The **Extra Delay Box** allows you to set time delay in seconds upto one second for each time step.\n" + "Now finally we set some matplotlib parameters to adjust how our plot will look like. The font is necessary because the Black Queen Unicode character is not a part of all fonts. You can move the slider to experiment and observe how the queens are assigned. It is also possible to move the slider using arrow keys or to jump to the value by directly editing the number with a double click. The **Visualize Button** will automatically animate the slider for you. The **Extra Delay Box** allows you to set time delay in seconds of upto one second for each time step." ] }, { @@ -2958,26 +2914,28 @@ "outputs": [ { "data": { - "image/png": "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\nRK5CYII=\n", + "application/vnd.jupyter.widget-view+json": { + "model_id": "fa243795d27f47c0af2cd12cbefa5e52", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ - "" + "interactive(children=(IntSlider(value=0, description='iteration', max=473, step=0), Output()), _dom_classes=('…" ] }, "metadata": {}, "output_type": "display_data" }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "The installed widget Javascript is the wrong version. It must satisfy the semver range ~2.1.4.\n" - ] - }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "516a8bb7f00d48a0b208c3f69a6f887d" - } + "model_id": "bdea801600cb441697ea3a810cb747a9", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(ToggleButton(value=False, description='Visualize'), ToggleButtons(description='Extra Del…" + ] }, "metadata": {}, "output_type": "display_data" @@ -3010,9 +2968,7 @@ { "cell_type": "code", "execution_count": 51, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "conflicts_instru_queen = make_instru(twelve_queens_csp)\n", @@ -3022,9 +2978,7 @@ { "cell_type": "code", "execution_count": 52, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "conflicts_step = make_plot_board_step_function(conflicts_instru_queen)" @@ -3034,7 +2988,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The visualization has same features as the above. But here it also highlights the conflicts by labeling the conflicted queens with a red background." + "This visualization has same features as the one above; however, this one also highlights the conflicts by labeling the conflicted queens with a red background." ] }, { @@ -3044,26 +2998,28 @@ "outputs": [ { "data": { - "image/png": "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\nTkSuQmCC\n", + "application/vnd.jupyter.widget-view+json": { + "model_id": "3bf64b599e5e4f128da23ecce08f3f53", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ - "" + "interactive(children=(IntSlider(value=0, description='iteration', max=52, step=0), Output()), _dom_classes=('w…" ] }, "metadata": {}, "output_type": "display_data" }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "The installed widget Javascript is the wrong version. It must satisfy the semver range ~2.1.4.\n" - ] - }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "29f5dba226b3492383ad768b54876588" - } + "model_id": "e4ccaba569f34a78857f2de8af4f01f2", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(ToggleButton(value=False, description='Visualize'), ToggleButtons(description='Extra Del…" + ] }, "metadata": {}, "output_type": "display_data" @@ -3100,7 +3056,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.5" } }, "nbformat": 4, From 46caa5e46fa9fa3c74ecdc3ad9132e45ab34953d Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Tue, 2 Oct 2018 05:25:57 +0500 Subject: [PATCH 10/15] Text Changes + Colored Table (#964) Made a colored table to display dog movement instead. Corrected grammatical errors, improved the sentence structure and corrected any typos found. --- agents.ipynb | 403 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 296 insertions(+), 107 deletions(-) diff --git a/agents.ipynb b/agents.ipynb index 026dd895e..6bfb34d98 100644 --- a/agents.ipynb +++ b/agents.ipynb @@ -17,11 +17,16 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": true, - "scrolled": true - }, - "outputs": [], + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Can't find a valid program for BlindDog, falling back to default.\n" + ] + } + ], "source": [ "from agents import *\n", "\n", @@ -39,7 +44,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "What we have just done is create a dog who can only feel what's in his surrounding environment (since he's blind), and can eat or drink. Let's see if he's alive..." + "What we have just done is create a dog who can only feel what's in his location (since he's blind), and can eat or drink. Let's see if he's alive..." ] }, { @@ -82,9 +87,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Food(Thing):\n", @@ -95,7 +98,7 @@ "\n", "class Park(Environment):\n", " def percept(self, agent):\n", - " '''prints & return a list of things that are in our agent's surrounding environemnt'''\n", + " '''prints & return a list of things that are in our agent's location'''\n", " things = self.list_things_at(agent.location)\n", " return things\n", " \n", @@ -134,7 +137,7 @@ }, "source": [ "# PROGRAM - BlindDog #\n", - "Now that we have a Park Class, we need to implement a program module for our dog. A program controls how the dog acts in it's environment; it will be very simple, and it's functionality is illustrated in the table below.\n", + "Now that we have a Park Class, we need to implement a program module for our dog. A program controls how the dog acts upon it's environment. Our program will be very simple, and is shown in the table below.\n", "
\n", " \n", " \n", @@ -155,9 +158,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class BlindDog(Agent):\n", @@ -167,13 +168,13 @@ " self.location += 1\n", " \n", " def eat(self, thing):\n", - " '''returns True for success and False otherwise'''\n", + " '''returns True upon success or False otherwise'''\n", " if isinstance(thing, Food):\n", " return True\n", " return False\n", " \n", " def drink(self, thing):\n", - " ''' returns True for success and False otherwise'''\n", + " ''' returns True upon success or False otherwise'''\n", " if isinstance(thing, Water):\n", " return True\n", " return False\n", @@ -289,7 +290,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This is how to implement an agent, its program, and environment. However, this was a very simple case. Lets now try a 2-Dimensional environment with multiple agents.\n", + "This is how to implement an agent, its program, and environment. However, this was a very simple case. Let's try a 2-Dimentional environment now with multiple agents.\n", "\n", "\n", "# 2D Environment #\n", @@ -301,9 +302,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Park2D(XYEnvironment):\n", @@ -347,13 +346,13 @@ " self.location[1] += 1\n", " \n", " def eat(self, thing):\n", - " '''returns True for success and False otherwise'''\n", + " '''returns True upon success or False otherwise'''\n", " if isinstance(thing, Food):\n", " return True\n", " return False\n", " \n", " def drink(self, thing):\n", - " ''' returns True for success and False otherwise'''\n", + " ''' returns True upon success or False otherwise'''\n", " if isinstance(thing, Water):\n", " return True\n", " return False\n", @@ -421,11 +420,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This works, but our blind dog doesn't make any use of the 2 dimensional space available to him. Lets make our dog more energetic so that instead of always moving down, he turns and moves forward as well. To be able to handle this extra motion, we'll need to make appropriate changes to our environment.\n", + "This works, but our blind dog doesn't make any use of the 2 dimensional space available to him. Let's make our dog more energetic so that he turns and moves forward, instead of always moving down. We'll also need to make appropriate changes to our environment to be able to handle this extra motion.\n", "\n", "# PROGRAM - EnergeticBlindDog #\n", "\n", - "Let's make our dog turn or move forward at random - except when he's at the edge of our park - in which case we make him change his direction explicitly by turning to avoid trying to leave the park. Our dog is blind, however, so he wouldn't know which way to turn - he'd just have to try arbitrarily.\n", + "Let's make our dog turn or move forwards at random - except when he's at the edge of our park - in which case we make him change his direction explicitly by turning to avoid trying to leave the park. Our dog is blind, however, so he wouldn't know which way to turn - he'd just have to try arbitrarily.\n", "\n", "
Percept:
\n", " \n", @@ -460,9 +459,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "from random import choice\n", @@ -491,13 +488,13 @@ " self.direction = self.direction + d\n", " \n", " def eat(self, thing):\n", - " '''returns True for success and False otherwise'''\n", + " '''returns True upon success or False otherwise'''\n", " if isinstance(thing, Food):\n", " return True\n", " return False\n", " \n", " def drink(self, thing):\n", - " ''' returns True f success and False otherwise'''\n", + " ''' returns True upon success or False otherwise'''\n", " if isinstance(thing, Water):\n", " return True\n", " return False\n", @@ -534,9 +531,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class Park2D(XYEnvironment):\n", @@ -601,26 +596,26 @@ "name": "stdout", "output_type": "stream", "text": [ - "dog started at [0,0], facing down. Lets see if he found any food or water!\n", - "EnergeticBlindDog decided to move downwards at location: [0, 0]\n", - "EnergeticBlindDog decided to move downwards at location: [0, 1]\n", - "EnergeticBlindDog drank Water at location: [0, 2]\n", - "EnergeticBlindDog decided to turnright at location: [0, 2]\n", - "EnergeticBlindDog decided to move leftwards at location: [0, 2], but couldn't\n", - "EnergeticBlindDog decided to turnright at location: [0, 2]\n", - "EnergeticBlindDog decided to turnright at location: [0, 2]\n", - "EnergeticBlindDog decided to turnleft at location: [0, 2]\n", - "EnergeticBlindDog decided to turnleft at location: [0, 2]\n", - "EnergeticBlindDog decided to move leftwards at location: [0, 2], but couldn't\n", - "EnergeticBlindDog decided to turnleft at location: [0, 2]\n", - "EnergeticBlindDog decided to turnright at location: [0, 2]\n", - "EnergeticBlindDog decided to move leftwards at location: [0, 2], but couldn't\n", - "EnergeticBlindDog decided to turnleft at location: [0, 2]\n", - "EnergeticBlindDog decided to move downwards at location: [0, 2], but couldn't\n", - "EnergeticBlindDog decided to turnright at location: [0, 2]\n", - "EnergeticBlindDog decided to turnleft at location: [0, 2]\n", - "EnergeticBlindDog decided to turnleft at location: [0, 2]\n", - "EnergeticBlindDog decided to move rightwards at location: [0, 2]\n", + "dog started at [0,0], facing down. Let's see if he found any food or water!\n", + "EnergeticBlindDog decided to turnright at location: [0, 0]\n", + "EnergeticBlindDog decided to move leftwards at location: [0, 0], but couldn't\n", + "EnergeticBlindDog decided to turnright at location: [0, 0]\n", + "EnergeticBlindDog decided to turnright at location: [0, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [0, 0]\n", + "EnergeticBlindDog decided to move upwards at location: [0, 0], but couldn't\n", + "EnergeticBlindDog decided to turnleft at location: [0, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [0, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [0, 0]\n", + "EnergeticBlindDog decided to turnright at location: [0, 0]\n", + "EnergeticBlindDog decided to turnright at location: [0, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [0, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [0, 0]\n", + "EnergeticBlindDog decided to move rightwards at location: [0, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [1, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [1, 0]\n", + "EnergeticBlindDog decided to turnleft at location: [1, 0]\n", + "EnergeticBlindDog decided to move downwards at location: [1, 0]\n", + "EnergeticBlindDog decided to move downwards at location: [1, 1]\n", "EnergeticBlindDog ate Food at location: [1, 2]\n" ] } @@ -649,9 +644,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "class GraphicPark(GraphicEnvironment):\n", @@ -716,7 +709,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 14, "metadata": { "scrolled": true }, @@ -725,13 +718,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "dog started at [0,0], facing down. Lets see if he found any food or water!\n" + "dog started at [0,0], facing down. Let's see if he found any food or water!\n" ] }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -747,10 +740,20 @@ "EnergeticBlindDog decided to move downwards at location: [0, 0]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -766,10 +769,20 @@ "EnergeticBlindDog drank Water at location: [0, 1]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -782,13 +795,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnleft at location: [0, 1]\n" + "EnergeticBlindDog decided to move downwards at location: [0, 1]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -801,13 +824,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnright at location: [0, 1]\n" + "EnergeticBlindDog decided to move downwards at location: [0, 2]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -820,13 +853,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to move downwards at location: [0, 1]\n" + "EnergeticBlindDog decided to move downwards at location: [0, 3]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -839,13 +882,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnleft at location: [0, 2]\n" + "EnergeticBlindDog decided to turnleft at location: [0, 4]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -858,13 +911,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to move rightwards at location: [0, 2]\n" + "EnergeticBlindDog decided to turnright at location: [0, 4]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -877,13 +940,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog ate Food at location: [1, 2]\n" + "EnergeticBlindDog decided to move downwards at location: [0, 4], but couldn't\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -896,13 +969,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnleft at location: [1, 2]\n" + "EnergeticBlindDog decided to turnright at location: [0, 4]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -915,13 +998,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnleft at location: [1, 2]\n" + "EnergeticBlindDog decided to move leftwards at location: [0, 4], but couldn't\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -934,13 +1027,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnleft at location: [1, 2]\n" + "EnergeticBlindDog decided to turnright at location: [0, 4]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -953,13 +1056,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to move downwards at location: [1, 2]\n" + "EnergeticBlindDog decided to turnleft at location: [0, 4]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -972,13 +1085,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnright at location: [1, 3]\n" + "EnergeticBlindDog decided to turnright at location: [0, 4]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -991,13 +1114,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to move leftwards at location: [1, 3]\n" + "EnergeticBlindDog decided to move upwards at location: [0, 4]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1010,13 +1143,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to move leftwards at location: [0, 3], but couldn't\n" + "EnergeticBlindDog decided to move upwards at location: [0, 3]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1029,13 +1172,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnleft at location: [0, 3]\n" + "EnergeticBlindDog decided to turnleft at location: [0, 2]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1048,13 +1201,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnright at location: [0, 3]\n" + "EnergeticBlindDog decided to turnleft at location: [0, 2]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1067,13 +1230,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to move leftwards at location: [0, 3], but couldn't\n" + "EnergeticBlindDog decided to turnright at location: [0, 2]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1086,13 +1259,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to turnright at location: [0, 3]\n" + "EnergeticBlindDog decided to move leftwards at location: [0, 2], but couldn't\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1105,13 +1288,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "EnergeticBlindDog decided to move upwards at location: [0, 3]\n" + "EnergeticBlindDog decided to turnright at location: [0, 2]\n" ] }, + { + "data": { + "text/html": [], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1154,10 +1347,8 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": { - "collapsed": true - }, + "execution_count": 15, + "metadata": {}, "outputs": [], "source": [ "from ipythonblocks import BlockGrid\n", @@ -1198,13 +1389,13 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -1217,7 +1408,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[], [], [], [], [, None]]\n", + "[[], [None], [], [None], [None]]\n", "Forward\n" ] } @@ -1229,9 +1420,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [] } @@ -1252,7 +1441,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.1" + "version": "3.6.5" } }, "nbformat": 4, From 2d78c1fa9e4a0844bf0825e7849365398b062ead Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Thu, 4 Oct 2018 09:42:59 +0500 Subject: [PATCH 11/15] Fixed typos (#970) Typos and minor other text errors removed --- knowledge_FOIL.ipynb | 65 ++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/knowledge_FOIL.ipynb b/knowledge_FOIL.ipynb index e06f5abf1..63e943416 100644 --- a/knowledge_FOIL.ipynb +++ b/knowledge_FOIL.ipynb @@ -38,7 +38,7 @@ "source": [ "## OVERVIEW\n", "\n", - "Like the [learning module](https://github.com/aimacode/aima-python/blob/master/learning.ipynb), this chapter focuses on methods for generating a model/hypothesis for a domain. Unlike though the learning chapter, here we use prior knowledge to help us learn from new experiences and find a proper hypothesis.\n", + "Like the [learning module](https://github.com/aimacode/aima-python/blob/master/learning.ipynb), this chapter focuses on methods for generating a model/hypothesis for a domain; however, unlike the learning chapter, here we use prior knowledge to help us learn from new experiences and to find a proper hypothesis.\n", "\n", "### First-Order Logic\n", "\n", @@ -46,7 +46,7 @@ "\n", "### Representation\n", "\n", - "In this module, we use dictionaries to represent examples, with keys the attribute names and values the corresponding example values. Examples also have an extra boolean field, 'GOAL', for the goal predicate. A hypothesis is represented as a list of dictionaries. Each dictionary in that list represents a disjunction. Inside these dictionaries/disjunctions we have conjunctions.\n", + "In this module, we use dictionaries to represent examples, with keys being the attribute names and values being the corresponding example values. Examples also have an extra boolean field, 'GOAL', for the goal predicate. A hypothesis is represented as a list of dictionaries. Each dictionary in that list represents a disjunction. Inside these dictionaries/disjunctions we have conjunctions.\n", "\n", "For example, say we want to predict if an animal (cat or dog) will take an umbrella given whether or not it rains or the animal wears a coat. The goal value is 'take an umbrella' and is denoted by the key 'GOAL'. An example:\n", "\n", @@ -69,7 +69,7 @@ "source": [ "# Inductive Logic Programming (FOIL)\n", "\n", - "Inductive logic programming (ILP) combines inductive methods with the power of first-order representations, concentrating in particular on the representation of hypotheses as logic programs. The general knowledge-based induction problem is to solve the entailment constrant:

\n", + "Inductive logic programming (ILP) combines inductive methods with the power of first-order representations, concentrating in particular on the representation of hypotheses as logic programs. The general knowledge-based induction problem is to solve the entailment constraint:

\n", "$ Background ∧ Hypothesis ∧ Descriptions \\vDash Classifications $\n", "\n", "for the __unknown__ $Hypothesis$, given the $Background$ knowledge described by $Descriptions$ and $Classifications$.\n", @@ -83,13 +83,13 @@ "use first-order literals instead of attributes, and the $Hypothesis$ is a set of clauses (set of first order rules, where each rule is similar to a Horn clause) instead of a decision tree.
\n", "\n", "\n", - "The FOIL algorithm learns new rules, one at a time, in order to cover all given possitive and negative examples.
\n", + "The FOIL algorithm learns new rules, one at a time, in order to cover all given positive and negative examples.
\n", "More precicely, FOIL contains an inner and an outer while loop.
\n", - "- __outer loop__: (function __foil()__) add rules untill all positive examples are covered.
\n", + "- __outer loop__: (function __foil()__) add rules until all positive examples are covered.
\n", " (each rule is a conjuction of literals, which are chosen inside the inner loop)\n", " \n", " \n", - "- __inner loop__: (function __new_clause()__) add new literals untill all negative examples are covered, and some positive examples are covered.
\n", + "- __inner loop__: (function __new_clause()__) add new literals until all negative examples are covered, and some positive examples are covered.
\n", " - In each iteration, we select/add the most promising literal, according to an estimate of its utility. (function __new_literal()__)
\n", " \n", " - The evaluation function to estimate utility of adding literal $L$ to a set of rules $R$ is (function __gain()__) : \n", @@ -102,14 +102,16 @@ " - Calculate the extended examples for the chosen literal (function __extend_example()__)
\n", " (the set of examples created by extending example with each possible constant value for each new variable in literal)\n", " \n", - "- Finally the algorithm returns a disjunction of first order rules (= conjuction of literals)\n", + "- Finally, the algorithm returns a disjunction of first order rules (= conjuction of literals)\n", "\n" ] }, { "cell_type": "code", "execution_count": 2, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [ { "data": { @@ -262,7 +264,6 @@ " share_vars = variables(clause[0])\n", " for l in clause[1]:\n", " share_vars.update(variables(l))\n", - " # creates literals with different order every time \n", " for pred, arity in self.pred_syms:\n", " new_vars = {standardize_variables(expr('x')) for _ in range(arity - 1)}\n", " for args in product(share_vars.union(new_vars), repeat=arity):\n", @@ -277,23 +278,36 @@ "\n", " return max(literals, key = partial(self.gain , examples = examples))\n", "\n", + "\n", " def gain(self, l ,examples):\n", - " pre_pos= len(examples[0])\n", - " pre_neg= len(examples[1])\n", - " extended_examples = [sum([list(self.extend_example(example, l)) for example in examples[i]], []) for i in range(2)]\n", - " post_pos = len(extended_examples[0]) \n", - " post_neg = len(extended_examples[1]) \n", - " if pre_pos + pre_neg ==0 or post_pos + post_neg==0:\n", + " """\n", + " Find the utility of each literal when added to the body of the clause. \n", + " Utility function is: \n", + " gain(R, l) = T * (log_2 (post_pos / (post_pos + post_neg)) - log_2 (pre_pos / (pre_pos + pre_neg)))\n", + "\n", + " where: \n", + " \n", + " pre_pos = number of possitive bindings of rule R (=current set of rules)\n", + " pre_neg = number of negative bindings of rule R \n", + " post_pos = number of possitive bindings of rule R' (= R U {l} )\n", + " post_neg = number of negative bindings of rule R' \n", + " T = number of possitive bindings of rule R that are still covered \n", + " after adding literal l \n", + "\n", + " """\n", + " pre_pos = len(examples[0])\n", + " pre_neg = len(examples[1])\n", + " post_pos = sum([list(self.extend_example(example, l)) for example in examples[0]], []) \n", + " post_neg = sum([list(self.extend_example(example, l)) for example in examples[1]], []) \n", + " if pre_pos + pre_neg ==0 or len(post_pos) + len(post_neg)==0:\n", " return -1\n", " # number of positive example that are represented in extended_examples\n", " T = 0\n", " for example in examples[0]:\n", - " def represents(d):\n", - " return all(d[x] == example[x] for x in example)\n", - " if any(represents(l_) for l_ in extended_examples[0]):\n", + " represents = lambda d: all(d[x] == example[x] for x in example)\n", + " if any(represents(l_) for l_ in post_pos):\n", " T += 1\n", - " value = T * (log(post_pos / (post_pos + post_neg) + 1e-12,2) - log(pre_pos / (pre_pos + pre_neg),2))\n", - " #print (l, value)\n", + " value = T * (log(len(post_pos) / (len(post_pos) + len(post_neg)) + 1e-12,2) - log(pre_pos / (pre_pos + pre_neg),2))\n", " return value\n", "\n", "\n", @@ -302,8 +316,7 @@ " List of omitted examples is returned."""\n", " uncovered = []\n", " for example in examples:\n", - " def represents(d):\n", - " return all(d[x] == example[x] for x in example)\n", + " represents = lambda d: all(d[x] == example[x] for x in example)\n", " if any(represents(l) for l in extended_examples):\n", " self.tell(subst(example, target))\n", " else:\n", @@ -408,7 +421,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Parent(x, y), [Mother(x, y)]], [Parent(x, y), [Father(x, y)]]]\n" + "[[Parent(x, y), [Father(x, y)]], [Parent(x, y), [Mother(x, y)]]]\n" ] } ], @@ -430,7 +443,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Suppose that we have some possitive and negative results for the relation 'GrandParent(x,y)' and we want to find a set of rules that satisfies the examples.
\n", + "Suppose that we have some positive and negative results for the relation 'GrandParent(x,y)' and we want to find a set of rules that satisfies the examples.
\n", "One possible set of rules for the relation $Grandparent(x,y)$ could be:
\n", "![title](images/knowledge_FOIL_grandparent.png)\n", "
\n", @@ -448,7 +461,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[Grandparent(x, y), [Parent(x, v_5), Parent(v_5, y)]]]\n" + "[[Grandparent(x, y), [Parent(x, v_6), Parent(v_6, y)]]]\n" ] } ], @@ -610,7 +623,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.3" + "version": "3.6.5" } }, "nbformat": 4, From 1584933f17933fa24c8c0486f395e9a416a24cd1 Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Thu, 4 Oct 2018 09:45:30 +0500 Subject: [PATCH 12/15] Fixed Typos (#971) Corrected typos + minor other text changes --- knowledge_current_best.ipynb | 37 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/knowledge_current_best.ipynb b/knowledge_current_best.ipynb index 68cb4e0e5..757062587 100644 --- a/knowledge_current_best.ipynb +++ b/knowledge_current_best.ipynb @@ -38,15 +38,15 @@ "source": [ "## OVERVIEW\n", "\n", - "Like the [learning module](https://github.com/aimacode/aima-python/blob/master/learning.ipynb), this chapter focuses on methods for generating a model/hypothesis for a domain. Unlike though the learning chapter, here we use prior knowledge to help us learn from new experiences and find a proper hypothesis.\n", + "Like the [learning module](https://github.com/aimacode/aima-python/blob/master/learning.ipynb), this chapter focuses on methods for generating a model/hypothesis for a domain; however, unlike the learning chapter, here we use prior knowledge to help us learn from new experiences and find a proper hypothesis.\n", "\n", "### First-Order Logic\n", "\n", - "Usually knowledge in this field is represented as **first-order logic**, a type of logic that uses variables and quantifiers in logical sentences. Hypotheses are represented by logical sentences with variables, while examples are logical sentences with set values instead of variables. The goal is to assign a value to a special first-order logic predicate, called **goal predicate**, for new examples given a hypothesis. We learn this hypothesis by infering knowledge from some given examples.\n", + "Usually knowledge in this field is represented as **first-order logic**; a type of logic that uses variables and quantifiers in logical sentences. Hypotheses are represented by logical sentences with variables, while examples are logical sentences with set values instead of variables. The goal is to assign a value to a special first-order logic predicate, called **goal predicate**, for new examples given a hypothesis. We learn this hypothesis by infering knowledge from some given examples.\n", "\n", "### Representation\n", "\n", - "In this module, we use dictionaries to represent examples, with keys the attribute names and values the corresponding example values. Examples also have an extra boolean field, 'GOAL', for the goal predicate. A hypothesis is represented as a list of dictionaries. Each dictionary in that list represents a disjunction. Inside these dictionaries/disjunctions we have conjunctions.\n", + "In this module, we use dictionaries to represent examples, with keys being the attribute names and values being the corresponding example values. Examples also have an extra boolean field, 'GOAL', for the goal predicate. A hypothesis is represented as a list of dictionaries. Each dictionary in that list represents a disjunction. Inside these dictionaries/disjunctions we have conjunctions.\n", "\n", "For example, say we want to predict if an animal (cat or dog) will take an umbrella given whether or not it rains or the animal wears a coat. The goal value is 'take an umbrella' and is denoted by the key 'GOAL'. An example:\n", "\n", @@ -73,15 +73,15 @@ "\n", "### Overview\n", "\n", - "In **Current-Best Learning**, we start with a hypothesis and we refine it as we iterate through the examples. For each example, there are three possible outcomes. The example is consistent with the hypothesis, the example is a **false positive** (real value is false but got predicted as true) and **false negative** (real value is true but got predicted as false). Depending on the outcome we refine the hypothesis accordingly:\n", + "In **Current-Best Learning**, we start with a hypothesis and we refine it as we iterate through the examples. For each example, there are three possible outcomes: the example is consistent with the hypothesis, the example is a **false positive** (real value is false but got predicted as true) and the example is a **false negative** (real value is true but got predicted as false). Depending on the outcome we refine the hypothesis accordingly:\n", "\n", - "* Consistent: We do not change the hypothesis and we move on to the next example.\n", + "* Consistent: We do not change the hypothesis and move on to the next example.\n", "\n", "* False Positive: We **specialize** the hypothesis, which means we add a conjunction.\n", "\n", "* False Negative: We **generalize** the hypothesis, either by removing a conjunction or a disjunction, or by adding a disjunction.\n", "\n", - "When specializing and generalizing, we should take care to not create inconsistencies with previous examples. To avoid that caveat, backtracking is needed. Thankfully, there is not just one specialization or generalization, so we have a lot to choose from. We will go through all the specialization/generalizations and we will refine our hypothesis as the first specialization/generalization consistent with all the examples seen up to that point." + "When specializing or generalizing, we should make sure to not create inconsistencies with previous examples. To avoid that caveat, backtracking is needed. Thankfully, there is not just one specialization or generalization, so we have a lot to choose from. We will go through all the specializations/generalizations and we will refine our hypothesis as the first specialization/generalization consistent with all the examples seen up to that point." ] }, { @@ -138,7 +138,7 @@ "source": [ "### Implementation\n", "\n", - "As mentioned previously, examples are dictionaries (with keys the attribute names) and hypotheses are lists of dictionaries (each dictionary is a disjunction). Also, in the hypothesis, we denote the *NOT* operation with an exclamation mark (!).\n", + "As mentioned earlier, examples are dictionaries (with keys being the attribute names) and hypotheses are lists of dictionaries (each dictionary is a disjunction). Also, in the hypothesis, we denote the *NOT* operation with an exclamation mark (!).\n", "\n", "We have functions to calculate the list of all specializations/generalizations, to check if an example is consistent/false positive/false negative with a hypothesis. We also have an auxiliary function to add a disjunction (or operation) to a hypothesis, and two other functions to check consistency of all (or just the negative) examples.\n", "\n", @@ -148,7 +148,9 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [ { "data": { @@ -370,7 +372,7 @@ "\n", "We will take a look at two examples. The first is a trivial one, while the second is a bit more complicated (you can also find it in the book).\n", "\n", - "First we have the \"animals taking umbrellas\" example. Here we want to find a hypothesis to predict whether or not an animal will take an umbrella. The attributes are `Species`, `Rain` and `Coat`. The possible values are `[Cat, Dog]`, `[Yes, No]` and `[Yes, No]` respectively. Below we give seven examples (with `GOAL` we denote whether an animal will take an umbrella or not):" + "Earlier, we had the \"animals taking umbrellas\" example. Now we want to find a hypothesis to predict whether or not an animal will take an umbrella. The attributes are `Species`, `Rain` and `Coat`. The possible values are `[Cat, Dog]`, `[Yes, No]` and `[Yes, No]` respectively. Below we give seven examples (with `GOAL` we denote whether an animal will take an umbrella or not):" ] }, { @@ -427,7 +429,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We got 5/7 correct. Not terribly bad, but we can do better. Let's run the algorithm and see how that performs." + "We got 5/7 correct. Not terribly bad, but we can do better. Lets now run the algorithm and see how that performs in comparison to our current result. " ] }, { @@ -472,7 +474,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[{'Rain': '!No', 'Species': 'Cat'}, {'Rain': 'Yes', 'Coat': 'Yes'}, {'Coat': 'Yes', 'Species': 'Cat'}]\n" + "[{'Species': 'Cat', 'Rain': '!No'}, {'Species': 'Dog', 'Coat': 'Yes'}, {'Coat': 'Yes'}]\n" ] } ], @@ -563,7 +565,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Say our initial hypothesis is that there should be an alternative option and let's run the algorithm." + "Say our initial hypothesis is that there should be an alternative option and lets run the algorithm." ] }, { @@ -613,7 +615,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "[{'Pat': '!Full', 'Alt': 'Yes'}, {'Hun': 'No', 'Res': 'No', 'Rain': 'No', 'Pat': '!None'}, {'Fri': 'Yes', 'Type': 'Thai', 'Bar': 'No'}, {'Fri': 'No', 'Type': 'Italian', 'Bar': 'Yes', 'Alt': 'No', 'Est': '0-10'}, {'Fri': 'No', 'Bar': 'No', 'Est': '0-10', 'Type': 'Thai', 'Rain': 'Yes', 'Alt': 'No'}, {'Fri': 'Yes', 'Bar': 'Yes', 'Est': '30-60', 'Hun': 'Yes', 'Rain': 'No', 'Alt': 'Yes', 'Price': '$'}]\n" + "[{'Alt': 'Yes', 'Type': '!Thai', 'Hun': '!No', 'Bar': '!Yes'}, {'Alt': 'No', 'Fri': 'No', 'Pat': 'Some', 'Price': '$', 'Type': 'Burger', 'Est': '0-10'}, {'Rain': 'Yes', 'Res': 'No', 'Type': '!Burger'}, {'Alt': 'No', 'Bar': 'Yes', 'Hun': 'Yes', 'Pat': 'Some', 'Price': '$$', 'Rain': 'Yes', 'Res': 'Yes', 'Est': '0-10'}, {'Alt': 'No', 'Bar': 'No', 'Pat': 'Some', 'Price': '$$', 'Est': '0-10'}, {'Alt': 'Yes', 'Hun': 'Yes', 'Pat': 'Full', 'Price': '$', 'Res': 'No', 'Type': 'Burger', 'Est': '30-60'}]\n" ] } ], @@ -627,6 +629,13 @@ "source": [ "It might be quite complicated, with many disjunctions if we are unlucky, but it will always be correct, as long as a correct hypothesis exists." ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -645,7 +654,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.3" + "version": "3.6.5" } }, "nbformat": 4, From a99dfffd44d5e8a4b32dc747c8e51a2f250b8767 Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Thu, 4 Oct 2018 09:45:56 +0500 Subject: [PATCH 13/15] Update intro.ipynb (#969) --- intro.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intro.ipynb b/intro.ipynb index 738ffb53d..93019595f 100644 --- a/intro.ipynb +++ b/intro.ipynb @@ -62,7 +62,7 @@ "source": [ "From there, the notebook alternates explanations with examples of use. You can run the examples as they are, and you can modify the code cells (or add new cells) and run your own examples. If you have some really good examples to add, you can make a github pull request.\n", "\n", - "If you want to see the source code of a function, you can open a browser or editor and see it in another window, or from within the notebook you can use the IPython magic function `%psource` (for \"print source\") or the function `psource` from `notebook.py`. Also, if the algorithm has pseudocode, you can read it by calling the `pseudocode` function with input the name of the algorithm." + "If you want to see the source code of a function, you can open a browser or editor and see it in another window, or from within the notebook you can use the IPython magic function `%psource` (for \"print source\") or the function `psource` from `notebook.py`. Also, if the algorithm has pseudocode available, you can read it by calling the `pseudocode` function with the name of the algorithm passed as a parameter." ] }, { From 4378fb2b3fc0701bd0251e30488bef6a90f21b26 Mon Sep 17 00:00:00 2001 From: Nouman Ahmed <35970677+Noumanmufc1@users.noreply.github.com> Date: Thu, 4 Oct 2018 09:46:29 +0500 Subject: [PATCH 14/15] Added activation functions (#968) --- learning.py | 23 ++++++++++++++++++++--- utils.py | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/learning.py b/learning.py index 399654073..8369e9633 100644 --- a/learning.py +++ b/learning.py @@ -4,7 +4,8 @@ removeall, unique, product, mode, argmax, argmax_random_tie, isclose, gaussian, dotproduct, vector_add, scalar_vector_product, weighted_sample_with_replacement, weighted_sampler, num_or_str, normalize, clip, sigmoid, print_table, - open_data, sigmoid_derivative, probability, norm, matrix_multiplication, relu, relu_derivative + open_data, sigmoid_derivative, probability, norm, matrix_multiplication, relu, relu_derivative, + tanh, tanh_derivative, leaky_relu, leaky_relu_derivative, elu, elu_derivative ) import copy @@ -746,8 +747,15 @@ def BackPropagationLearner(dataset, net, learning_rate, epochs, activation=sigmo # The activation function used is relu or sigmoid function if node.activation == sigmoid: delta[-1] = [sigmoid_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] - else: + elif node.activation == relu: delta[-1] = [relu_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] + elif node.activation == tanh: + delta[-1] = [tanh_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] + elif node.activation == elu: + delta[-1] = [elu_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] + else: + delta[-1] = [leaky_relu_derivative(o_nodes[i].value) * err[i] for i in range(o_units)] + # Backward pass h_layers = n_layers - 2 @@ -762,9 +770,18 @@ def BackPropagationLearner(dataset, net, learning_rate, epochs, activation=sigmo if activation == sigmoid: delta[i] = [sigmoid_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) for j in range(h_units)] - else: + elif activation == relu: delta[i] = [relu_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) for j in range(h_units)] + elif activation == tanh: + delta[i] = [tanh_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) + for j in range(h_units)] + elif activation == elu: + delta[i] = [elu_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) + for j in range(h_units)] + else: + delta[i] = [leaky_relu_derivative(layer[j].value) * dotproduct(w[j], delta[i+1]) + for j in range(h_units)] # Update weights for i in range(1, n_layers): diff --git a/utils.py b/utils.py index a514a67eb..c0c92aec8 100644 --- a/utils.py +++ b/utils.py @@ -9,6 +9,7 @@ import random import math import functools +import numpy as np from itertools import chain, combinations @@ -273,9 +274,47 @@ def sigmoid(x): """Return activation value of x with sigmoid function""" return 1 / (1 + math.exp(-x)) + + +def relu_derivative(value): + if value > 0: + return 1 + else: + return 0 + +def elu(x, alpha=0.01): + if x > 0: + return x + else: + return alpha * (math.exp(x) - 1) + +def elu_derivative(value, alpha = 0.01): + if value > 0: + return 1 + else: + return alpha * math.exp(value) + +def tanh(x): + return np.tanh(x) + +def tanh_derivative(value): + return (1 - (value ** 2)) + +def leaky_relu(x, alpha = 0.01): + if x > 0: + return x + else: + return alpha * x + +def leaky_relu_derivative(value, alpha=0.01): + if value > 0: + return 1 + else: + return alpha + def relu(x): return max(0, x) - + def relu_derivative(value): if value > 0: return 1 From 59fa07ce33a2ddaf4fd5f6c8015c81202fbc6085 Mon Sep 17 00:00:00 2001 From: Muhammad Junaid <34795347+mkhalid1@users.noreply.github.com> Date: Thu, 4 Oct 2018 09:49:15 +0500 Subject: [PATCH 15/15] Updated label_queen_conflicts function (#967) Shortened it, finding conflicts separately and storing them in different variables has no use later in the notebook; so i believe this looks better. --- csp.ipynb | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/csp.ipynb b/csp.ipynb index fcf8b5867..411d6f55c 100644 --- a/csp.ipynb +++ b/csp.ipynb @@ -2821,19 +2821,13 @@ "def label_queen_conflicts(assignment,grid):\n", " ''' Mark grid with queens that are under conflict. '''\n", " for col, row in assignment.items(): # check each queen for conflict\n", - " row_conflicts = {temp_col:temp_row for temp_col,temp_row in assignment.items() \n", - " if temp_row == row and temp_col != col}\n", - " up_conflicts = {temp_col:temp_row for temp_col,temp_row in assignment.items() \n", - " if temp_row+temp_col == row+col and temp_col != col}\n", - " down_conflicts = {temp_col:temp_row for temp_col,temp_row in assignment.items() \n", - " if temp_row-temp_col == row-col and temp_col != col}\n", + " conflicts = {temp_col:temp_row for temp_col,temp_row in assignment.items() \n", + " if (temp_row == row and temp_col != col\n", + " or (temp_row+temp_col == row+col and temp_col != col)\n", + " or (temp_row-temp_col == row-col and temp_col != col)}\n", " \n", " # Place a 3 in positions where this is a conflict\n", - " for col, row in row_conflicts.items():\n", - " grid[col][row] = 3\n", - " for col, row in up_conflicts.items():\n", - " grid[col][row] = 3\n", - " for col, row in down_conflicts.items():\n", + " for col, row in conflicts.items():\n", " grid[col][row] = 3\n", "\n", " return grid\n",