{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2a468658",
   "metadata": {},
   "source": [
    "# Randomness in python\n",
    "Generating random numbers is a non-trivial task. How can you ask a computer or a machine to generate a range of numbers with no bias ? Even for human beings, it is impossible to be completely random, in fact there are often unwanted correlations that appear.\n",
    "\n",
    "In this exercise sery we will explore the random generation of number through different means"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bead736",
   "metadata": {},
   "source": [
    "### Preambule\n",
    "In this exercise we will start representing the data in plots and histograms.\n",
    "The python package we will be using is called **matplotlib.pyplot** and you can import it using `import matplotlib.pyplot as plt`.\n",
    "You will become familiar with matplotlib along the year. \\\n",
    "To show a simple plot of a set of data, you may use `plt.plot(data)` or `plt.plot(data_x, data_y)`. Many customisation option are available for you to explore online.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e2793d2",
   "metadata": {},
   "source": [
    "### Generation of numbers\n",
    "In this exercise, we will generate a large set of pseudo-random numbers by hand and represent them to see how accurately we are able to be random.\n",
    "Keeping 8 fingers on the 1-8 numbers on your keyboard, hit the keys as randomly as possible until you have created a string containing at least 500 digits (this will take about 1 minute). You can convert this set of numbers to a numpy array using\n",
    "`a = np.array(list(str(3974582739452...871263)),dtype=int)`.\n",
    "\n",
    "- Use np.size(a) to check how long your array is, show it's minimum and maximum and the first 10 numbers.\n",
    "- Use the numpy function `np.random.randint(lower, upper, size)` to create an array of random numbers of the same length.\n",
    "- Compute the mean and standard deviation of both arrays. Are they what you expect?\n",
    "- Plot a histogram of a and b ( use plt.hist([a,b],bins=np.arange(1-0.5,8+1+0.5)) to get the right bin centers). Does it fit with what you expect?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "95b8bfde",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3f574178",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Manual Number\n",
    "\n",
    "plt.hist([array1,array2],bins=np.arange(1-.5,8+1.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ba41855",
   "metadata": {},
   "source": [
    "Take you sets of random number and look at the distance between consecutive numbers. You may use the fact that `a[1:]` is the array `a` taken from the SECOND entry to the end and `a[:-1]` is from the first entry to the penultimate entry.\\\n",
    "Represent this data as a histogram.\n",
    "What do you notice ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "plt.hist([array1_distance,array2_distance],bins=np.arange(-7.5,7+1.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08157b46",
   "metadata": {},
   "source": [
    "### Manipulating Data with Numpy\n",
    "In this exercise, we will train with manipulating data with numpy arrays.\n",
    "- Create three numpy arrays of random integers from 1 to 8 (inclusive), containing 100, 1000 and 10000 entries. Use np.random.randint() for this.\n",
    "- Assume these numbers are temperatures in °C – convert them to °F, then compute the mean & SD of the resulting array. How does this compare to converting the old mean & SD directly?\n",
    "- Now assume these numbers are currents in amperes. Compute the corresponding power consumed by a kettle with a resistance of 20Ohm (use Ohm’s law and P=V*I). How does the mean & SD of the resulting array of powers compare to converting the mean & SD directly?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "09a6c89e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create Numpy arrays of random numbers\n",
    "\n",
    "\n",
    "# Assume Celsius and convert to Fahr.\n",
    "# https://en.wikipedia.org/wiki/Fahrenheit gives us : Tf =  Tc * 9/5 + 32. \n",
    "\n",
    "\n",
    "\n",
    "# Assume Amperes\n",
    "# Ohm's Law : V = R * I\n",
    "# Power : P = V * I = R * I²\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3007e197",
   "metadata": {},
   "source": [
    "### The Random Walk\n",
    "The random walk is a simple model of great importance for understanding phenomena such as thermal transport, charge diffusion, the stock market, bacteria and many others.\n",
    "\n",
    "Write a script that implements a random walk. That is, starting from 0, at every step decide randomly, with a 50:50 probability to either increase the number by one (move right) or reduce it by one (move left), and find the value after n-steps.\n",
    "- As a first stage, implement this with a for-loop. Start with a list containing only [0] and append randomly a number that is either one larger or one smaller than the previous number. To make a random choice, you can use np.random.randint(2), which gives you a random integer that is either 0 or 1.\n",
    "- Make a line-plot showing the position of one trajectory after 100 steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f010dab",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Plot the different steps of the walk\n",
    "plt.plot(walk)\n",
    "plt.xlabel('Number of steps')\n",
    "plt.ylabel('Random walk position')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "536710a4",
   "metadata": {},
   "source": [
    "To be more efficient (and later scale to larger dimensions), instead of running a loop, you can directly use the np.random.randint() function to give you an array of random numbers. For documentation and more examples, see https://numpy.org/doc/stable/reference/random/generated/numpy.random.randint.html .\n",
    "\n",
    "For example, if you use `np.random.randint(2,size=100)*2-1`, you will get an array of 100 numbers which are either +1 or -1 (we have scaled the 0,1 that randint gives us).\n",
    "\n",
    "To then get the position after i steps, you can use the `np.cumsum()` function (but be careful to not miss the starting point – you can use np.concatenate() to add it).\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4ab7716",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "145863bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "plt.plot(walk)\n",
    "plt.xlabel('Number of steps')\n",
    "plt.ylabel('Random walk position')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "169dd7d3",
   "metadata": {},
   "source": [
    "Now let's consider making this experiments many times. Instead of using for loops, we can use numpy to create multi-dimensional arrays (matrices) where each line is a new execution of the experiment.\n",
    "- Using `size=(10,100)` in `randint` you can generate 10 trajectories at once – but make sure you sum over the right axis in cumsum. Plot the result to check."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "543e0a75",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "plt.plot(walks.T)\n",
    "plt.xlabel('Number of steps')\n",
    "plt.ylabel('Random walk position')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "816958f1",
   "metadata": {},
   "source": [
    "- Create 1000 trajectories, and plot a histogram of their final positions after (a) 10 steps, (b) 100 steps. (c)1000 steps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36f0b945",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "plt.hist(final_states_after_10_steps, alpha=0.5, label='After 10 steps')\n",
    "plt.hist(final_states_after_100_steps, alpha=0.5, label='After 100 steps')\n",
    "plt.hist(final_states_after_1000_steps, alpha=0.5, label='After 1000 steps')\n",
    "plt.xlabel('Final position')\n",
    "plt.ylabel('Frequency')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35155506",
   "metadata": {},
   "source": [
    "- Compute the mean and standard deviation (SD) of those distributions of final positions after 10 / 100 / 1000 steps."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74e03a68",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "d11336fd",
   "metadata": {},
   "source": [
    "- Plot the SD as a function of step number"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "70b58e45",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.plot(..)\n",
    "plt.xlabel(\"Standard Deviation\")\n",
    "plt.ylabel(\"Step Number\")\n",
    "plt.legend()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19e764dd",
   "metadata": {},
   "source": [
    "### Extra : Random Walk with different probabilities\n",
    "Up to now we had an equal probability to move up or down. Try now to use numpy to be able to move either up or down with a certain probability. Try 60%-40% then 80%-20%.\n",
    "Again plot the SD and the mean as a function of step size. What type of function do you recognize, and how does the slope depend on probability?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "593a6a76",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "plt.plot(walks.T)\n",
    "plt.xlabel('Number of steps')\n",
    "plt.ylabel('Random walk position')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4664d4a",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
