```python
%matplotlib inline
import numpy as np
import pylab as pl
import sympy as sym
from sympy.functions import Abs
#from sympy import Abs, Symbol, S
```

## Goals of today:

- Check how good or bad are the estimates given in the theoretical lecture
- Compute errors, plot error tables
- Compare Equispaced with Chebyshev

## Lagrangian part

The estimate we want to check:

$$
|| f -p || \leq ||f^{n+1}||_\infty \frac{||w(x)||_\infty}{(n+1)!}
$$

in order to do so we need to define, simbolic and numerical functions. [`Sympy`]() is a very useful package to handle symbolic expressions and to export them to numerical functions. A the beginnnig of this notebook it is imported with the command: `import sympy as sym`.

Let's start by defining a way to compute the $\|\cdot\|_\infty$ norm, in an approximate way, using numpy.

We use an approximate way which is based on the computation of the $l^\infty$ norm on large n-dimensional vectors, that we use to evaluate and plot our functions.

Begin by defining a linear space, used to evaluate our functions.

```python
# Using directly numpy broadcasting and max function
l_infty = lambda y: abs(y).max()

# Using full python lists and ranges
def l_infty_1(y):
    m = -1.0
    for i in range(len(y)):
        m = max(m, abs(y[i]))
    return m

# Iterating over numpy array entries
def l_infty_2(y):
    m = -1.0
    for i in y:
        m = max(m, abs(i))
    return m

# Using numpy norm function
l_infty_3 = lambda y: np.linalg.norm(y, ord=np.inf)

# Test it on a random vector of a million elements
yy = np.random.rand(int(1e6))

%timeit l_infty(yy)
%timeit l_infty_1(yy)
%timeit l_infty_2(yy)
%timeit l_infty_3(yy)

# The timings show that the manual numpy solution is the most efficient.
# The first version and the last do the exact same thing,
# with the difference that the last has some overheads due to
# parsing of the optional parameters, which is not there in the first version
```

    4.37 ms ± 499 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    571 ms ± 51.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    493 ms ± 47.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    3.45 ms ± 491 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In order to compute derivatives and evaluate symbolic functions, we use sympy. Let's construct a symbolic variable, and define functions in terms of it:

```python
# Now construct a symbolic function...
t = sym.var('t')
fs = 1.0/(1.0+t**2) # Runge function
print(fs)
fs.diff(t, 1)
```

    1.0/(t**2 + 1.0)

$\displaystyle - \frac{2.0 t}{\left(t^{2} + 1.0\right)^{2}}$

To make this function *digestible* by numpy we use the simple command `nf = sym.lambdify(t,f, 'numpy')`. This allows the function nf to be called with numpy arguments.

```python
# Create a vector for the evaluation of functions, norms, etc.
x = np.linspace(-5,5, 2**10+1)

# This won't work
# ns(x)

nf = sym.lambdify(t, fs, 'numpy')
nfprime = sym.lambdify(t, fs.diff(t,1), 'numpy')

# Now we can plot and evaluate the function on numpy arrays
_ = pl.plot(x,nf(x))
_ = pl.plot(x,nfprime(x))
```

Now we construct a small helper function that given a symbolic expression with one single free symbol, it returns a numpy aware lambda function of its n-th derivative, that can be used to evaluate numpy expressions.

```python
def der(f,n):
    assert len(f.free_symbols) == 1, "I can only do this for single variable functions..."
    t = f.free_symbols.pop()
    return sym.lambdify(t, f.diff(t, n), 'numpy')

f = der(fs, 0)
fp = der(fs, 1)
fpp = der(fs, 2)

# Stack columns and plot them all together
_ = pl.plot(x, np.c_[f(x), fp(x), fpp(x)])
```

```python
# Check derivatives for two functions...
function_set = [fs, sym.sin(2*sym.pi*t)]

for my_f in function_set:
    print("****************")
    print(my_f)
    for i in range(5):
        print(l_infty(der(my_f,i)(x)))
```

    ****************
    1.0/(t**2 + 1.0)
    1.0
    0.6495170207333653
    2.0
    4.668260905616381
    24.0
    ****************
    sin(2*pi*t)
    1.0
    6.283185307179586
    39.47841760435743
    248.05021344239853
    1558.5454565440386

We aim at controlling all of the pieces of the inequality above, plot how terms behave with the degree, and see what happens `:)`

Good thing is to start from the beginning and control the term $||f-p||_\infty$. We recall that:
$$
p = \mathcal{L}^n f := \sum_{i=0}^n f(x_i) l^n_i(x),
$$

with

$$
l^n_i(x) := \prod_{j=0, j\neq i}^n \frac{(x-x_j)}{(x_i-x_j)}  \qquad
i = 0, \dots, n.
$$

Let's implment this guy. We want to fill the matrix `Ln` with $n+1$ rows and as many colums as the number of points where we evaluate the funtion.

$$
\text{Ln}_{ij} := l_i(x_j)
$$

so that

$$
\text{Ln}_{ij} f(q_i) = \sum_{i=0}^{n} l_i(x_j)f(q_i) = (\mathcal{L}^n f)(x_j)
$$

A good idea would be to collect the few operations in a function, like this one:
```python
def lagrangian_interpolation_matrix(x,q):
    ...
    return Ln
```
so that we can recall it whenever we need it.

**Hint:** I wouldn't call myself a good programmer, but I do my best to be like that. First construct the code in the main section of your program, run it, check that it works, then collect the precious commmands you wrote in an function.

### Step 0

```python
n = 3
q = np.linspace(-5,5,n+1)

Ln = np.zeros((n+1, len(x)))

for i in range(n+1):
    Ln[i] = np.ones_like(x)
    for j in range(n+1):
        if j != i:
            Ln[i] *= (x-q[j])/(q[i]-q[j])

    # Alternative one-liner...
    # Ln[i] = np.product([(x-q[j])/(q[i]-q[j]) for j in range(n+1) if j != i], axis=0)

_ = pl.plot(x, Ln.T)
```

## Step 1

Now we transform this into two function that takes the points where we want to compute the
matrix, and the interpolation points we use to define the basis.

```python
def lagrangian_interpolation_matrix(x,q):
    Ln = np.zeros((len(q), len(x)))

    for i in range(len(q)):
        Ln[i] = np.ones_like(x)
        for j in range(len(q)):
            if j != i:
                Ln[i] *= (x-q[j])/(q[i]-q[j])
    return Ln

def lagrangian_interpolation_matrix_one_liner(x,q):
    Ln = np.zeros((len(q), len(x)))
    for i in range(len(q)):
        Ln[i] = np.product([(x-q[j])/(q[i]-q[j]) for j in range(len(q)) if j != i], axis=0)
    return Ln

```

```python
Error = lagrangian_interpolation_matrix(x,q) - lagrangian_interpolation_matrix_one_liner(x,q)

print("Error:", np.linalg.norm(Error))
```

    Error: 0.0

From the previous lecture we know that the mathemathical expression:
$$
(\mathcal{L}^nf)(x_i) := \sum_{j=0}^n f(q_j) l^n_j(x_i) = (\text{Ln}^T f)_i
$$

Can be easyly translated into the `numpy` line:
```python
Ln.T.dot(f(x))
```
Let's give it a try:

```python
fs = sym.sin(t)
print(fs)
f = der(fs,0)

n = 3
q = np.linspace(-5,5,n+1)

Ln = lagrangian_interpolation_matrix(x,q)

_ = pl.plot(x, f(x)) #blue
_ = pl.plot(x, Ln.T.dot(f(q)))
_ = pl.plot(q, f(q), 'or') #dots

e = f(x) - Ln.T.dot(f(q))
_ = pl.plot(x, e) #green

Error = l_infty(e)

print("Error:", Error)
```

Let's increase the number of points...

```python
q = np.linspace(-5,5,15)
Ln = lagrangian_interpolation_matrix(x,q)

_ = pl.plot(x, f(x))
_ = pl.plot(x, Ln.T.dot(f(q)))
_ = pl.plot(q, f(q), 'ro')

e = f(x) - Ln.T.dot(f(q))

Error = l_infty(e)

print("Error:", Error)
```

Now compute the following

$||f-p||_\infty = $ `error`

$||f^{n+1}||_\infty = $ `nth_der`

$w(x) =\prod_{i=0}^n (x-q_i), \quad ||w(x)||_\infty = $ `w`

```python
# define w
w = lambda x,q: np.product([x-q[i] for i in range(len(q))], axis=0)

q = np.linspace(-5,5,5)

Ln = lagrangian_interpolation_matrix(x,q)
error = l_infty(f(x) - Ln.T.dot(f(q)))

fs = sym.sin(2*sym.pi*t)
fp = der(fs, len(q))

nth_der = l_infty(fp(x))
w_infty = l_infty(w(x,q))

UpperEstimate = nth_der*w_infty/np.math.factorial(len(q))
print(UpperEstimate)
```

    28939.83544506955

```python
fs = sym.sin(t)

points = range(2,15)
UpperEstimate = []
ActualError = []

for n in points:
    q = np.linspace(-5,5,n)

    Ln = lagrangian_interpolation_matrix(x,q)
    ActualError.append(l_infty(f(x) - Ln.T.dot(f(q))))

    fp = der(fs, len(q))
    nth_der = l_infty(fp(x))

    w_infty = l_infty(w(x,q))

    UpperEstimate.append(nth_der*w_infty/np.math.factorial(len(q)))

print(UpperEstimate)

_ = pl.semilogy(points, UpperEstimate)
_ = pl.semilogy(points, ActualError) #segmented line
```

If I didn't mess the code this a good spot to play aroud with the function to be checked. Let's save everything into a single function. Let's also look forward. Instead of using by default equispaced points, lets' ask for a function that can generate the points for us, given x and q...

```python
def check_errors(x, fs, n, generator=lambda x,n: np.linspace(x.min(),x.max(),n)):
    """
    Check the error for the interpolation obtained by calling
    q = generator(x,i)
    for each i in the range(2,n).

    The default generator uses equispaced points.
    """
    points = range(2,n)
    UpperEstimate = []
    ActualError = []

    f = der(fs, 0)

    for n in points:
        q = generator(x,n)

        Ln = lagrangian_interpolation_matrix(x,q)
        ActualError.append(l_infty(f(x) - Ln.T.dot(f(q))))

        fp = der(fs, len(q))
        nth_der = l_infty(fp(x))

        w_infty = l_infty(w(x,q))

        UpperEstimate.append(nth_der*w_infty/np.math.factorial(len(q)))

    return (points, UpperEstimate, ActualError)
```

```python
function_set = [1/(1+t**2), sym.sin(2*sym.pi*t), sym.sin(t)]

for fs in function_set:
    p, u, e = check_errors(x, fs, 20)

    _ = pl.semilogy(p, np.c_[u, e])
    pl.title(str(fs))
    pl.legend(['Estimate', 'Actual'])
    pl.show()
```

Now let's try to repeat the same thing with **Chebyshev** points...

```python
# We start by trying to interpolate the Runge function:

# Get a smaller sample of points...
x = np.linspace(-5,5,2**6+1)

# This is used to generate chebyshev points between xmin and xmax
generator = lambda x,n: x.min()+(x.max()-x.min())/2*(np.polynomial.chebyshev.chebgauss(n)[0]+1)

# 10 interpolation points
q = generator(x,10)

# The the Runge function
f = der(function_set[0], 0)

# The interpolation matrix
Ln = lagrangian_interpolation_matrix(x,q)

_ = pl.plot(x, np.c_[f(x), Ln.T.dot(f(q))])
_ = pl.plot(q, f(q), 'or')
```

```python
for fs in function_set:
    p, u, e = check_errors(x, fs, 20, generator)

    _ = pl.semilogy(p, np.c_[u, e])
    pl.title(str(fs))
    pl.legend(['Estimate', 'Actual'])
    pl.show()
```


# Chapter 1 Exercises
In this notebook we will go through the exercises of chapter 1 of Introduction to Stochastic Processes with R by Robert Dobrow.

```R
library(ggplot2)
```

### 1.1
For the following scenarios identify the stochastic process {$X_t, t \in I$}, describing (i) $X_t$ in context (ii) state space, and (iii) index Set. State whether the state space and index are discrete or continuous.

    b) At the end of each year, a 4-year college student either advances in grade repeats the grade or drops out
    Answer:
let $X_t$ denote the state of the student at the end of the year $t$. This is a discrete space with $S=$ {Advances, Repeats, Drop out} for $I=${1,2,...}

    c) Seismologists record daily earthquake magnitudes in Chile. the largest recorded earthquake in history was the Valdivia, Chile earthquake on May 22 1960, which had a magnitude of 9.5 on Richter scale.
Answer:
ley $X_t$ the magnitude of earthquake t. and with continuous Space $S =$ (0,9.5] and $I=$ {0,1,2,...}

    d)Data are kept on circumferences of trees in an arboretum. The arboretum covers a two square-mile area.
    Answer:
ley $X_t$ the circumference of tree t. and with continuous Space $S =$ (0,max(circum)] and $I=$ {0,1,2,...}

    e) Starting Monday morning at 9 a.m., as students arrive to class, the teacher records the student arrival times. The class has 30 students and lasts for 60 minutes.
    Answer:
ley $X_t$ arrival time of student t. and with continuous Space $S =$ $[0,60]$ and $I=$ {0,1,2,...,30}

    f) A card player shuffles a standard deck of cards by the following method: the top card of the deck is placed somewhere in the deck at random. The player does this 100 times to mix up the deck.
    Answer:
ley $X_t$ the order of the cards at shuffle t. and with Discrete Space $S =$ 52! and $I=$ {0,1,2,...,100}

### 1.2
A regional Insurance company insures homeowners against flood damage. Half of their policyholders are in florida 30% in Louisiana, and 20% in Texas. Company Actuaries Inc. give the estimate table 1.1 for the probability that a policy holder will file a claim for flood damage over the next year.

    a) find the probability that a random policyholder will file a claim for flood damage next year.
    b) a claim was filed. Find the probability that the policyholder is from texas
|  | Table 1.1 Probability of claim for flood damage | |
| --- | :-: | --- |
| Florida | Louisiana | Texas |
| 0.03 | 0.015 | 0.02 |

#### Answer
using the law of total probability, then we need to calculate
$$P(claim)=\sum_{state} P(claim|State)*P(State)$$

```R
prob_a = 0.5*0.03+0.3*0.015+0.2*0.02
prob_a
```

0.0235

In the second case we want to find the probability that it comes from texas given that a claim happened, then we want:
$$P(Texas|claim)=\frac{P(claim \cap Texas)}{P(claim)}=\frac{P(claim|Texas)P(Texas)}{P(claim)}$$

```R
0.2*0.02/prob_a
```

0.170212765957447

### 1.3
let $B_1,...,B_k$ be a partition of the sample space. For events A and C, prove the law of total probability for conditional probability.
$$P(A|C)=\sum_{i=1}^{k}P(A|B_i \cap C)P(B_i|C)$$

#### Answer

To answer this let's expand the right part of the equalityt
\begin{align}
\sum_{i=1}^{k}P(A|B_i \cap C)P(B_i|C) & = \sum_{i=1}^{k}\frac{P(A \cap B_i \cap C)}{P(B_i \cap C)} \frac{P(B_i \cap C)}{P(C)} \\
 & = \sum_{i=1}^{k}\frac{P(A \cap B_i \cap C)}{P(C)} \\
 & = \frac{1}{P(C)}\sum_{i=1}^{k}P(A \cap B_i \cap C) \\
 & = \frac{P(A \cap C)}{P(C)} = P(A|C)
\end{align}

### 1.4
See exercise 1.2 Among all policyholders who live within five miles of the Atlantic Ocean, 75% live in florida, 20% in Louisiana and 5% live in Taxas. For thosewho live close to the ocean the probabilities of filing a claim increase as given in table 1.2.

Assume that the policyholder lives within five miles of the Atlantic Coast. Use the law of total probability fot conditional probability in Exercise 1.3 to find the chance that they will file a claim for flood damage next year.

|  | Table 1.2 Probability of claim for those within 5 miles | |
| --- | :-: | --- |
| Florida | Louisiana | Texas |
| 0.1 | 0.06 | 0.06 |

#### Answer
What we are looking for is:
$$P(claim|5m)=\sum_{state}P(claim | State \cap 5m)*P(State | 5m)$$

Note here that the partition is the state, that is why we are not looking to find the same result as before.

```R
prob_claimG5m = 0.1*0.75+0.06*0.2+0.06*0.05
prob_claimG5m
```

0.09

### 1.5
Two fais-sided dice are rolled let $X_1,X_2$ be the outcomes of the first and second die respectively.

- a) find the conditional distribution of $X_2$ given that $X_1+X_2=7$
- b) find the conditional distribution of $X_2$ given that $X_1+X_2=8$

#### Answer
we want $P(X_2|X_1+X_2=7)$ we can see that the values of $X_2$ are uniformly distributed {$1,2,3,4,5,6$} the reason for this is that if $X_1+X_2=7$, then if $X_1$ was 1, then $X_2$ needs to be 6, and so on with all the numbers that $X_1$ and $X_2$ can be.

in b) the solution comes from the same logic, but $X_2$ can never be 1 since $X_1$ would need to be 7 to make the sum 8, but 7 is not in the dice.

### 1.6
Bob has n coins in his pocket. One is two-headed, the rest are fair. A coin is picked at randomm flipped and shows heads. Find the probability that the coin is two headed.

#### Answer
Let's say that H is the probability of obtaining a heads and S is the probability of getting a two sided head coin, then what the want is the following:
$$P(S|H)=\frac{P(S \cap H)}{P(H)}$$
but we can see that $P(S \cap H) = P(S)$ Since you are always going to get the heads if you have the two sided head coin, then we just need to look for the $P(S)$ and $P(H)$ and
$$P(S) = 1/n$$
$$P(H) = \frac{1}{n}*1+\sum_{n-1}^{i=1}\frac{1}{n}*\frac{1}{2}=\frac{1}{n}+\frac{n-1}{2n} = \frac{n+1}{2n}$$
Then
$$P(S|H)=\frac{1/n}{n+1/(2n)}=\frac{2n}{n(n+1))}= \frac{2}{n+1}$$

```R
CoinFlipSim <- function(n = 10, sims = 1000){
    # Note that for n = 10 the probability 2/11=0.1818
    results = 0
    heads = 0
    for(i in 1:sims){
        coin = sample(1:10, 1)
        if(coin == 1){
            results = results + 1
            heads = heads + 1
        }else{
            if(runif(1) < 0.5){
                heads = heads + 1
            }
        }

    }
    return(results/heads)
}
CoinFlipSim(sims=10000)
```

0.175170679123248

### 1.7
A rat is trapped in a maze with three doors and some hidden cheese. If the rat takes door one, he will wander around the maze for 2 minutes and return to where he started. If he takes door two, he will wander around the maze for 3 munutes and return to where he started. If he takes door three, he will find the cheese after one minute.  If the rat returns to where he started, then picks a new door. He picks a door by random, how long in average will it take it to get the cheese.

#### Answer
The key point here is to see that the expected value can be seen as a summ of the conditional expectations (due to the law of total expectation, then let $X$ be the time to reach the cheese and Y the door the rat chose we have:
\begin{align}
E(X) &= E(E(X|Y)) \\
 & = E(X|1)P(X=1)+E(X|2)P(X=2)+E(X|3)P(X=3) \\
 & = (2+E(X))1/3+(3+E(X))1/3+1/3 \\
 & = 2/3+1+1/3+2E(X)/3
\end{align}

Then $E(X)=6$, it is important to note that the 2 and 3 and 1 come from the time it takes the rat to go back or eat the cheese and since it goes back to the beginning, then counting from there we have again the $E(X)$.

this means that the rat will get to the cheese in 6 minutes

```R
RatTimeMaze <- function(sims = 10000){
    results = c()
    for(i in 1:sims){
        time = ChooseDoor(0)
        results = c(results,time)
    }
    return(sum(results)/length(results))
}

ChooseDoor <- function(t){
    rand = runif(1)
    if(rand < 1/3){
        t=t+2
        return(ChooseDoor(t))
    }
    else if(rand < 2/3){
        t=t+3
        return(ChooseDoor(t))
    }
    else{
        t=t+1
        return(t)
    }
}
RatTimeMaze()
```

5.908

### 1.8
A bag contains 1 red, 3 green and 5 yellow balls. A sample of four balls is picked. Let $G$ be the number of green balls in the sample. Let $Y$ be the number of yellow balls in the sample.

- a) Find the conditional probability mass function of $G$ given $Y=2$ assyming the sample is picked without replacement.
- b) Find the conditional probability mass function of $G$ given $Y=2$ assyming the sample is picked with replacement.

#### Answer
Since there have already been picked 2, then we only need to check for three cases $G=0,1,2$. In this case since we are looking for without replacement, then
$$P(G=g|Y=2)=\frac{P(G=g \cap Y=2)}{P(Y=2)}$$
Let's calculate $P(Y=2)$
$$P(Y=2)==\frac{{5 \choose 2}{4 \choose 2}}{{9 \choose 4}}$$
and
$$P(G=g \cap Y=2)==\frac{{5 \choose 2}{3 \choose g}{1 \choose 2-g}}{{9 \choose 4}}$$
when $1-g \geq 1$
Then we see that when $G=0$ this does not work, and both equations have the same denominator, then
$$P(G=1|Y=2)=\frac{{5 \choose 2}{3 \choose 1}{1 \choose 1}}{{5 \choose 2}{4 \choose 2}}$$
$$P(G=2|Y=2)=\frac{{5 \choose 2}{3 \choose 2}}{{5 \choose 2}{4 \choose 2}}$$

```R
p1 = choose(3,1)/choose(4,2)
p2 = choose(3,2)/choose(4,2)
sprintf("%f, %f", p1, p2)
```

'0.500000, 0.500000'

There fore the answer is
$$P(G=1|Y=2)=0.5$$
$$P(G=2|Y=2)=0.5$$

For case b) we have that $P(Y=2)$ is a binomial distribution and $P(G=g \cap Y=2)$ is a multinomial distribution. Then
$$P(Y=2)={4 \choose 2}\biggl( \frac{5}{9} \biggr)^2 \biggl( \frac{4}{9} \biggr)^2$$
$$P(G=g \cap Y=2)=\frac{4!}{2!g!(2-g)!}\biggl( \frac{5}{9} \biggr)^2 \biggl( \frac{3}{9} \biggr)^g\biggl( \frac{1}{9} \biggr)^{2-g}$$
Then

```R
py = choose(4,2)*(5/9)**2*(4/9)**2
p0 = (factorial(4)/(factorial(2)*factorial(2))*(5/9)**2*(1/9)**2)/py
p1 = (factorial(4)/(factorial(2))*(5/9)**2*(1/9)*(3/9))/py
p2 = (factorial(4)/(factorial(2)*factorial(2))*(5/9)**2*(3/9)**2)/py
sprintf("%f, %f, %f ", p0, p1, p2)
```

'0.062500, 0.375000, 0.562500 '

There fore the answer is
$$P(G=0|Y=2)=0.0625$$
$$P(G=1|Y=2)=0.375$$
$$P(G=2|Y=2)=0.5625$$

## 1.9
Assume that X is uniformly distributed {$1,2,3,4$}. If X=x, then Y is uniformly distributed {$1,...,x$}. Find

- a)$P(Y=2|X=2)$
- b)$P(Y=2)$
- a)$P(X=2|Y=2)$
- a)$P(X=2)$
- a)$P(Y=2,X=2)$

### Answer
a) $P(Y=2|X=2) = 1/2$ since Y is uniformly distributed {$1,2$}

b) using the law of total probability:
$$P(Y=2) = P(Y=2|X=2)P(X=2)+ P(Y=2|X=3)P(X=3)+P(Y=2|X=4)P(X=4) = 1/2*1/4+1/3*1/4+1/4+1/4$$
which makes
$$P(Y=2) = 0.270833  $$

c) $P(X=2|Y=2) = \frac{P(Y=2,X=2)}{P(Y=2)} = \frac{P(Y=2|X=2)P(X=2)}{P(Y=2)}$
Then:
$$P(X=2|Y=2) = \frac{1/8}{0.2708} = 0.46154414208174865$$

d) $$P(X=2) = 1/4$$

c) $$P(Y=2,X=2) = 1/8$$

```R
1/8/0.27083
```

0.461544142081749

## 1.10
A die is rolled until a 3 occurs. By conditioning the outcome of the first roll, what is the probability that an even number of rolls is needed.

### Answer
Well, the condition just works for the case it is 3 the first roll in which case the probability is zero since it already happened. for the other cases the rolls should be independent of each other, so in this case the probability is the same as asking what is the probability of rolling a three in an odd number of rolls (since the first roll has already happened).

in this case to happen on roll number two the probability is $1/6$, for it to happen in 4 rolls it is $5^2/6^3$, in 6 rolls it is $5^4/6^5$. Here the pattern then is that the probability of obtaining this is $\sum_{i=0}^\infty 5^{ni}/6^{2i+1}$ which is equal to:

$$p = \frac{1}{6*(1-5^2/6^2)} = \frac{6}{11}$$

If this question is framed to be answered as in question 1.9, then the answer is different (it is known right now that the answer we are looking for is 5/11). First the probability of getting evens can be seen as:

$$P(Evens) = P(Evens|R=3)*P(R=3)+P(Evens|R\neq 3) *P(R\neq 3)$$

It is important to note that since the events of the rolls are independent, then the probability of geting even given that the first roll was not even, then the probability is equal to the probability of getting the roll is odds (as discussed before). This means that if $P(Evens)=p$, then:

$$p = 0*1/6+(1-p) *5/6=> 11/6p=5/6 => p=5/11$$

## 1.11
Consider the gambler's ruin process where at each wager the gambler wins with probability p and loses with probability q=1-p. the gambler stops when reaching n or losing all their money. if they start with k, with 0<k<n. What is the probability of eventual ruin?.

### Answer

Using the same logic as the one presented in the example we have that the $x_k=x_{k+1}p+x_{k-1}q$, but $x_{k+1}-x_k=(x_k-x_{k-1})p/q$. this means:

$$x_{k}-x_{k-1}=x_1(p/q)^k-1$$
and
$$x_{k}=\sum_{i=1}^k x_1(p/q)^k-1=x_1\frac{1-(p/q)^k}{1-(p/q)}$$

since we know that $x_n=1$, then:
$$1=x_1\frac{1-(p/q)^n}{1-(p/q)} => x_1=\frac{1-(p/q)}{1-(p/q)^n}$$

Which makes

$$x_{k}=\frac{1-(p/q)^k}{1-(p/q)^n}$$

## 1.12
In n rolls of a fair die, let X be the number of times 1 is rolled, and Y the number of times 2 is rolled. Find the conditional distribution of X given Y=y.

### Answer
here $P(X=x)$ is a binomial distribution, in the case of making it conditional to Y, it still is a binomial distribution since the rolls are independent from each other, but what changes is the amount of times the experiment is done and the probability of getting y, since we know the amount of times 2 has appeared, this means there are only 5 possible outcomes on the die, this means that:
$$P(X=x|Y=y)={n-y \choose x} (1/5)^x (4/5)^{n-y-x}$$

## 1.13
Random Variables X and Y have probability density function :

$$f(x,y)=3y,   for 0<x<y<1$$

find the conditionals density functions

### Answer
Here we do the integrals and we get:

$f_Y(y)=\int_0^y 3y dx = 3y^2, for$ $0<y<1$

$f_X(x)=\int_x^1 3y dy = 3/2(1-x)^2, for$ $0<x<1$

then:
$$f_{X|Y}(x|y)=1/y,  0<x<y$$
$$f_{Y|X}(y|x)=2y/(1-x)^2, x<y<1$$

note that the first conditional density is a uniform density function.

## 1.14
repear exercise 1.13, but with:

$$f(x,y)=4e^{-2x}, 0<y<x<\infty$$

### Answer
Doing the same as before

$f_X(x)=4xe^{-2x}, 0<x<\infty$

$f_y(y)=2e^{-2y}, 0<y<\infty$

then
$$f_{X|Y}(x|y)=2e^{-2x}/e^{-2y}, y<x<\infty$$
$$f_{Y|X}(y|x)=1/x, 0<y<x$$
The last density is a uniform density funciton.

## 1.15
Let X and Y be uniformly distributed in the circle of radius 1 at the origin. find the conditional density function of Y given X=x.

### Answer
The area of a cicle is defined around the bound x^2+y^2=1 and having a number picked x in the circle from 0-1, then it would delimit the y value to bounded by $-\sqrt{1-x^2}<y<\sqrt{1-x^2}$ and picking a value from here has to be uniform because of how the value is picked from the circle, then the result is given by:

$$f_{Y|X}(y|x)=\frac{1}{2\sqrt{1-x^2}}, -\sqrt{1-x^2}<y<\sqrt{1-x^2}$$

## 1.16
A poker hand consists of 5 cards from a standard 52-card deck. Find the expected number of aces in a poker hand given that the first one is an ace.

### Answer
Assuming that you draw the cards instantly after from the pool of 51 cards left, then, considering the random variable $X$ as the probability of obtaining $x$ aces in you hand and $Y$ the probability of getting an ace in the first draw, there are only 3 probabilities to calculate and the expected values is represented as follows:

$$E[X|Y=1] = 1 P(X=1|Y=1) + 2 P(X=2|Y=1) + 3 P(X=3|Y=1) + 4 P(X=4|Y=1)$$

Where $P(X=x|Y=1) = P(X=x \cap Y=1)/P(y)$

Then $P(Y=1)=1/13$, the probability of the first card being dealt being an ace. And:

$$P(X=x \cap Y=1) = \frac{1}{13}*\frac{{3 \choose x-1}*{48 \choose 3-(x-1)}}{{51 \choose 3}}$$
Then:
$$P(X=x | Y=1) = \frac{{3 \choose x-1}*{48 \choose 3-(x-1)}}{{51 \choose 3}}$$

```R
p1 = choose(3,0)*choose(48,3)/choose(51,3)
p2 = choose(3,1)*choose(48,2)/choose(51,3)
p3 = choose(3,2)*choose(48,1)/choose(51,3)
p4 = choose(3,3)*choose(48,0)/choose(51,3)
sprintf("%f, %f, %f, %f ", p1, p2, p3, p4)
```

'0.830540, 0.162497, 0.006915, 0.000048 '

Then the expected value is: 1.17 (calculated bellow)

```R
1*p1+2*p2+3*p3+4*p4
```

1.17647058823529

## 1.17
Let $X$ be a poisson random variable with $\lambda = 3$ then calculate $E[X|X>2]$

### Answer
the expected value is given by:
$$E[X|X>2] = \frac{\sum_{x=3}^{\infty}P(X=x)}{P(X>2)}$$

Then:

```R
lambda = 3
```

```R
p2e = 1 - (dpois(0, lambda)+dpois(1, lambda)+dpois(2, lambda))
p2e
```

0.576809918873156

```R
exp = 0
for(i in 3:1000) exp = exp + i*dpois(i, lambda)
exp/p2e
```

4.16524595187131

The expected value is then 4.16

## 1.18
From the definition of conditional expectation given an event, show that:

$$E[I_B|A]=P(B|A)$$

### Answer
Using the definition of conditional expectation and the fact that the indicator function is 1 where event B happens and 0 everywhere else, then:
$$E[I_B|A]= 0*P(B^c|A) + 1 * P(B|A) = P(B|A)$$

## 1.19
Get the variance of the event of getting two heads in a row.

### Answer
Saying that experiment A has probability p of happening (in this case getting heads) and experiment B has $q=1-p$ probability of happening, then:

If you wanted to calculate the probabilities, assuming $p$ is the probability of success and $q=1-p$, then the important thing is to notice is that for the experiment to work, then you need to have failed $x-2$ times the experiment and then have two consecutive experiments in the last two cases (similar to a geometric distribution).

In this case, the probability of failing up to $x-2$ can be seen as all the combinations of experiments where the sum of the exponents of $p$ and $q$ sumed is $x-2$ but the exponent in $p$ has to be smaller than that of $q$ (this is due to the fact that if experiment A happens more times than experiemtn B, then at some moment in the sequence experiment A had to have happened two times in a row). Another constriction is that experiment A cannot happen last in the x-2 first experiments (if this was true, then the experiment would end after x-1 instead of at x).

We need to ensure to not count the cases where p appears two consecutive times (or more) in the sequence. we accomplish this by getting ${x-2-k \choose k}$ (this can be proofed by deduction. Done bellow, but is not good and I believe it can be written better.)

[One could also use the probability generating function](https://math.stackexchange.com/questions/2845196/expected-number-of-coin-flips-for-2-consecutive-heads-for-first-time)

Then this means that

$$P(X=x) = \bigg( \sum^{\frac{x-2}{2}}_{k=0}{x-2-k \choose k} q^{x-2-k}*p^{k}\bigg) * p^2$$

proof by deduction:

suppose we want the combinations of cases where the events with probability $p$ and $q$ happen $n$ times in total, but with the restriction of p never appearing in consecutive cases and it cannot be in the last position, then the easiest way to think about this is to imagine two events that you are drawing from $q$ and $pq$, then we can see what happens in each case to form our case (it is not necessary, but is easier to understand if put on paper)

* i=0
In this case we have n q events happening and 0 pq, then we have n choose 0
$$f(n,p,q) = q*q*...*q = {n \choose 0} q^n$$
* i=1
For this case we are looking for the case where we hace n-2 q events and 1 pq event happening, then in total we have n-1 events happening. This means we have n-1 choose 1.
$$f(n,p,q) = p*q*...*q + q*p*...*q*q + ... + q*q*...*p*q = {n-1 \choose 1}*q^{n-1}*p^1$$
the reason why this is choose 1 in $n-1$ is because there are n-1 possible positions where 1 $p$ can be.
* i=2
For this case we are looking for the case where we hace n-4 q events and 2 pq event happening, then in total we have n-2 events happening. This means we have n-2 choose 2.
$$f(n,p,q) = {n-2 \choose 2}*q^{n-4}*(qp)^2 = {n-2 \choose 2}*q^{n-2}*p^2$$

getting the same logic as in the last case for the kth case we can say that
* i=k
$$f(n,p,q) = {n-k \choose k}*q^{n-k}*p^k$$ (where $k < n/2$)

(If you wanted to do a similar prove for the general case where the experiment ends after l events happening in the row, the same idea can be done as here, but with the events q, pq...q, p..pq and so on.)

```R
calculate_probability<-function(x, p=1/2){
    cumulative = 0
    if(x>1){

        for(i in 0:(floor((x-2)/2))){cumulative = cumulative + choose(x-2-i,i)*(1-p)**(x-2-i)*p**(i)}
        return(cumulative*p**2)
    }else{ return(0) }
}
```

```R
simulate_experiment<-function(sims, p=1/2){
    trials = c()
    for(i in 0:sims){
        counter = 0
        consecutive = 0
        while(T){
            counter=counter+1
            if(runif(1)<p){
                consecutive = consecutive+1
                }
            else{consecutive = 0}
            if(consecutive == 2) break
        }
        trials<-c(trials,counter)
    }
    return(c(mean(trials), var(trials)))
}
```

```R
expectation = 0
moment2 = 0
for(i in 0:1000){
    expectation = expectation + i*calculate_probability(i)
    moment2 = moment2 + i*i*calculate_probability(i)
}
```

```R

```

```R
print(moment2-expectation**2)
```

    [1] 22

```R
simulate_experiment(100000)
```

<ol class=list-inline>
	<li>6.01354986450136</li>
	<li>22.447466399336</li>
</ol>

## 1.20
A fair coin is flipped repitedly

    a) find the expected number of flips needed to get three heads in a row.

    b) find the expected number of flips to get k heads in a row.

### Answer

a) Let Y be the number of flips needed and H is the event of getting heads and T the event of getting Tails. Using the same logic as presented in exercise 1.21 in the book we can think that if a tails appears, then the expectarion is "reset" sort to speak, this means that:
$$E[Y] = P(T)E[Y|T] + P(HT)E[Y|HT] + P(HHT)E[Y|HHT] + P(HHH)E[Y|HHH]$$
Since the future tosses are independent of the past tosses, then:
$$E[Y] = 1/2*(E[Y] + 1) + 1/4*(E[Y] + 2) + 1/8*(E[Y] + 3) + 1/8*(3)$$
Saying $x = E[Y]$
$$x - 7/8x = 1 + 3/4 => x = (8)*(14/8) = 14$$

b) Using the same logic as before we can first write the first equation as:
$$E[Y] = P(T)E[Y|T] + P(HT)E[Y|HT] + ... + P(H...HT)E[Y|H...HT] + P(H...H)E[Y|H...H]$$
Then:
$$E[Y] = 1/2*(E[Y] + 1) + 1/4*(E[Y] + 2) + ... + 1/2^k*(E[Y] + k) + 1/2^k*(k)$$
$$E[Y] = \sum_{i=1}^{k}1/2^k *(E[Y]) + \sum_{i=1}^{k}k/2^k + k/2^k $$
where:
$$\sum_{i=1}^{k}1/2^k = 1/2*\frac{1-(1/2)^k}{1-1/2}=1-1/2^k= \frac{2^k-1}{2^k} $$
and
$$\sum_{i=1}^{k}k/2^k = 1/2 + 2*(1/2)^2 + 3*(1/2)^3 + ... + k*(1/2)^k = S$$
$=>$
$$(1-1/2)S= (1/2)^2 + 2*(1/2)^3 + 3*(1/2)^4 + ... + k*(1/2)^{k+1}$$
$=>$
$$(1-1/2)S= (1/2) + (1/2)^2 + (1/2)^3 + (1/2)^4 + ... + (1/2)^{k} - k*(1/2)^{k+1}$$
$=>$
$$(1-1/2)S= (1/2) \frac{1-(1/2)^k}{1-(1/2)} - k*(1/2)^{k+1}$$
$=> $ (just because in this case is 1/2 you can simplify this expression)
$$S=  \frac{1-(1/2)^k - k*(1/2)^{k+1}}{(1/2)} $$
This means that:
$$E[Y] = E[Y] * \frac{2^k-1}{2^k} + \frac{1-(1/2)^k - k*(1/2)^{k+1}}{(1/2)} + k/2^k $$
$=>$
$$E[Y] =  2^k\bigg(\frac{1-(1/2)^k - k*(1/2)^{k+1}}{(1/2)} + k/2^k\bigg) $$

## 1.21
Let T be a nonnegative, continuous random variable. Show:
$$E[T]=\int_0^\infty P(T>t)dt$$

### Answer
For this case using integration by parts, we have:

| type | u | v |
| --- | :-: | :-: |
| derivative | -f(t)dt | 1dt |
| non-der | P(T>t)=(1-F(T)) | t |

Then
$$\int_0^\infty P(T>t)dt = \bigg[ t*(1-F(t)) \bigg|_0^\infty + \int_0^\infty tf(t)dt =  1*(1-1)+0(1-0) + \int_0^\infty tf(t)dt = \int_0^\infty tf(t)dt = E[Y]$$

Therefore:
$$E[T]=\int_0^\infty P(T>t)dt$$

## 1.22
Find $E[X|Y]$ when $(X,Y)$ is uniformly distributed on the following  regions.

    a) The rectangle [a,b] x [c,d]
    b) The triangle with vertices (0,0)(0,1)(1,1)
    c) the disc of radius 1 centered at the origin)

### Answer
a) Imagine that you have the rectangle with the mentions in [a,b] x [c,d] with coordinates for $A:(x_1,y_2) B:(x_2,y_2) C:(x_1,y_1) D:(x_2,y_1)$, then imagine choosing a random number in $X$, then you end up with a region like in the next image:

This means that you are going to get Y this will be chosen at random in the segment $\overline{EF}$ This means that $X|Y$ is a uniform random variable from E to F, where their $y$ min and max are $y_1,y_2$. Then:

$$E[X|Y]= \frac{y_1+y_2}{2}$$

b)
In this case imagine you have the triangle like the one mentioned, then if a random number between 0,1 is chosen for x, and we know it, then the region for the distribution of $Y$ is given by the line segment $\overline{KJ}$ in the next figure

As it can be seen if the x value was different, then the line segment $\overline{KJ}$ would change based on the function $y=x$. This means that $Y|X=x$ is a uniform distribution $(0,x)$, then:

$$E[Y|X]=\frac{x}{2}$$

b)
In this case imagine you have the circle like the one mentioned, then if a random number between -1,1 is chosen for x, and we know it, then the region for the distribution of $Y$ is given by the line segment $\overline{LM}$ in the next figure

As it can be seen if the x value was different, then the line segment $\overline{LM}$ would change based on the functions $y=-\sqrt{x-1}$ and $y=\sqrt{x-1}$. This means that $Y|X=x$ is a uniform distribution $(-\sqrt{x-1},\sqrt{x-1})$, then:

$$E[Y|X]=\frac{\sqrt{x-1}-\sqrt{x-1}}{2}=0$$

if you had any circle with any radius in a different center, then the expected value would be the $y$ value of the center of the circle.

## 1.23
Let $X_1,...,X_n$ be i.i.d sequence of random variables, with comon means $\mu$. Let $S_n=X_1+...+X_n$ for $n\geq1$.

a) find $E[S_m|S_n]$ for $m\leq n$

a) find $E[S_m|S_n]$ for $m > n$

### Answer
a)
To get this case first let's see analize the sum.

$$E[S_n|S_n]= E[X_1+X_2+...+X_n|S_n] = E[X_1|S_n]+...+E[X_n|S_n]$$

but since $X_1,...,X_n$ are independent, then
$$S_n = nE[X_1|S_n] => E[X_1|S_n]=\frac{S_n}{n}$$

Then
$$E[S_m|S_n]= E[X_1+X_2+...+X_m|S_n] = E[X_1|S_n]+...+E[X_m|S_n]$$
$=>$
$$E[S_m|S_n]= m*E[X_1|S_n]=m\frac{S_n}{n}$$

b)
Looking again at the sum, then:
$$E[S_m|S_n]= E[X_1+X_2+...+X_n+...+X_m|S_n] = S_n + E[X_{n+1}|S_n]+...+E[X_m|S_n]$$
$=>$ since $X_{n+1},...,X_m$ are independent of $S_n$
$$E[S_m|S_n] = S_n + E[X_{n+1}]+...+E[X_m]= S_n + (m-n)\mu$$

## 1.24
Prove the law of total expectation $E[Y]=E[E[Y|X]]$ for the continous case.

### Answer

$$E[Y]=\int_{-\infty}^\infty yf_Y(y)dy$$
$=>$ then by the law of total probability
$$E[Y]=\int_{-\infty}^\infty y \int_{-\infty}^\infty f_{Y|x}(y|x) f_X(x) dxdy$$
$=>$ Then doing a change order of integration is done (assuming the integration area is not dependant between x and y)
$$E[Y]=\int_{-\infty}^\infty \bigg(\int_{-\infty}^\infty y f_{Y|x}(y|x)\bigg) f_X(x) dydx$$

## 1.25
suppose that you have $X$ and $Y$ independent exponential random variables wit respective parameters 1 and 2. Find $P(X/Y<3)$ by conditioning.

### Answer
using the conditioning, then:
$$P(X/Y<3)=P(X<3Y)=\int_0^\infty P(X<3y|Y)f_Y(y)dy = \int_0^\infty P(X<3y)f_Y(y)dy$$
$=>$
$$P(X/Y<3)=\int_0^\infty (1-e^{-3y})*2e^{-2y}dy=2\int_0^\infty (e^{-2y}-e^{-5y})dy = 1-\frac{2}{5}=3/5$$

## 1.26
The density of $X$ is $f(x)=xe^{-x}$, for $x>0$. Given $X=x$, $Y$ is uniformly distributed on $(0,x)$. Find the probability $P(Y<2)$.

### Answer
$$P(Y<2)=\int_2^\infty P(Y<2|X=x)f_X(x)dx=\int_2^\infty \frac{2}{x}xe^{-x}dx=2\int_2^\infty e^{-x}dx = 2e^{-2}=0.27$$

## 1.27
A restaurant Receives $N$ customers per day, where $N$ is a random variable with mean 200 and sd 40. The amount spent by each customer is normally distributed with mean $\$15 $ and sd $\$3$. The amounts that customers spend are independent of each other and idependent of other $N$. Find the mean and standard deviation of the total amount spent at the restaurant per day.

### Answer
What we have here is a random sum of random variables, so let's denote $S_N = X_1 + X_2 + ... + X_N$ there $X\sim \mathcal{N}(15,3)$ and N has unknown distribution with mean 200 and standard deviation 40. Then by the law of total expectation we have:
$$E[S_N]=E[E[X_1+...+X_n|N=n]]$$
Where
$$E[X_1+...+X_n|N=n] = nE[X_i] = 15*n$$
Then
$$E[S_N]= E[15*N] = 15 E[N] = 3000$$

Then applying the law of total variance:
$$Var(S_N) = E[Var(S_n|N=n)] + Var(E[S_n|N=n]) = E[N*Var(X_i)] + Var(15*N)= 9*E[N]+225*Var(N) = 1800+ 360000 = 361800$$
Then the standard deviation is $601.498$

## 1.28
On any day, the number of accidents on the highways has a poisson distribution with parameter $\Lambda$ the parameter $\Lambda$ varies from day to day and is itself a random variable. Find the mean and variance of the number of accidents per day when $\Lambda$ is uniform distributed on $(0,3)$.

### Answer
Also first applying the law of total expectation and having $X\sim Pois(\Lambda)$, then:

$$E[X]=E[E[X|\Lambda=\lambda]]=E[\Lambda]=3/2$$

Then applying the law of total Variance

$$Var(X)=E[Var(X|\Lambda=\lambda)]+Var(E[X|\Lambda=\lambda])=E[\Lambda]+Var(\Lambda)=3/2+9/12=9/4$$

## 1.29
If X and Y are independent, does $Var(Y|X)=Var(X)$?

### Answer
Yes. (No explanation evil laugh goes out of the room)...

The reason for this is because we just need to follow the math.
$$Var(Y|X)=E[Y^2|X] + E[Y|X]= \sum_{Y\in\Omega}Y^2P(Y|X)+\sum_{Y\in\Omega}YP(Y|X)$$

But is known that if $X$ and $Y$ are independent then $P(Y|X)=P(Y)$, thi means that
$$Var(Y|X)= \sum_{Y\in\Omega}Y^2P(Y|X)+\sum_{Y\in\Omega}YP(Y|X) = \sum_{Y\in\Omega}Y^2P(Y)+\sum_{Y\in\Omega}YP(Y)=Var(Y)$$

## 1.30
Assume that $Y=g(X)$ is a function of $X$. Find simple expressions for

a) $E[Y|X]$

b) $Var(Y|X)$

### Answer
Since $Y=g(X)$, then:
a)
$$E[Y|X]=E[g(x)|X=x]=g(x)$$
b)
$$Var(Y|X)=Var(g(x)|X=x)=0$$

## 1.31
Consider a sequence of i.i.d. Bernoulli trials with success parameter p. Let X be the number of trials needed until the first occurs. Then X has a geometric distribution with parameter $p$. Find the variance of $X$ by conditioning to the first trial

### Answer.

First let's check for the expectation, then the result is:
$$E[X|B]=(E[Y]+1)*(1-p)+p=(1/p+1)(1-p)+p=\frac{1+p}{p}(1-p)+p= \frac{1-p^2}{p}+p=\frac{1-p^2+p^2}{p}=\frac{1}{p}$$

Then, for the second moment, remember that the first trial is the only one dependent on the bernoulli trial, then:
$$E[X^2|B]= 1(p) + (1-p)\sum_{k=2}^\infty k^2 P(Y=k-1)=\frac{2-p}{p^2}$$

With this, then
$$Var(X|B)=\frac{2-p}{p^2}-\bigg(\frac{1}{p}\bigg)^2=\frac{1-p}{p^2}$$

## 1.32
Simulate flipping three fair coins and counting the number of heads $X$.
a) Use the simulation to estimate $P(X=1)$ and $E[X]$

### Answer

```R
ThreeCoinExp<-function(proba= 1/2, x, sim = 1000){
    "
    This fuction is derived from the class made in python in the other notebook
    is made to simulate the experiment in the problem it receives as input the probability of
    getting heads which will be used for the simulations.

    It has two functions P_x which receives the x which will be used to approximate the probability using
    the simulation
    Is important to remember that the underlying random variable is a binomial distribution with p = self.proba.
    "
    counter = 0
    results = c()
    # Might be better to have the simulation as an independent function (like in comming exercises)
    for(i in 0:sim){
        coins = runif(3)
        results = c(results,(sum(coins<proba)))
        if(results[length(results)]==x) counter=counter+1
    }
    return(c(counter/sim, sum(results)/length(results)))
}
```

```R
ThreeCoinExp(3/4,1,10000)
```

<ol class=list-inline>
	<li>0.14</li>
	<li>2.25147485251475</li>
</ol>

## 1.33
Cards are drawn from a standard deck, with replacement, until an ace appears. Simulate the mean and variance of the number of cards required.

### Answer

```R
CardExpWithReplacement<-function(proba=1/13, sim=1000){
    "
    Here is the class for the experiment mentioned above, still is important to mention the random variable
    described has a geometric distribution with p=1/13 since there are replacements.
    It takes as input the probability that you wanted to simulate for the geometric random variable.
    "
    results=c()
        for(i in 0:sim){
            counter = 0
            while(T){
                counter =counter+1
                if(runif(1)<proba){
                    results = c(results,counter)
                    break
                }
            }
        }
    return(c(mean(results), var(results)))
}
```

```R
CardExpWithReplacement(sim=10000)

```

<ol class=list-inline>
	<li>12.8421157884212</li>
	<li>152.658570082992</li>
</ol>

## 1.34
The time until the bus arrives has an exponential distribution with mean 30 minutes. simulate using a package and compare with the real probability

### Answer

```R
simulations = 1000
scale = 30
print(sum(rexp(simulations, rate=1/scale)<20)/simulations)
print(pexp(20, rate=1/scale))
```

    [1] 0.465
    [1] 0.4865829

## 1.35
See gamblersruin.R A gambler starts with $\$60$ initial stake.
Simulate the probability of the gambler winning $\$100$ before going broke when he has the probability of winning a) $p=0.5$ what about when b) $p=0.51$

### Answer

```R
randomWalk<-function(init_money=50 , prob=1/2, min_state=0, max_state=100, outcomes = c(-1,1)){
    "
    this is is the function that performs one random walk, if you want to perform the simulation you
    can run the gambling walk function beforehand.
    Take into account the taking variables, where the outcomes are the possible results of loosing or earning money
    the min and max state respresent the values where the simulation would stop.

    the outputs are sent as a list so the $ comand can be used.
    "
    money = init_money
    walk = c(money)
    win = F
    while (T){
        money = money+sample(outcomes, 1, prob = c(1-prob,prob))
        walk= c(walk, money)
        if ((money <= min_state) || (money >= max_state)){
            win=(money >= max_state)
            break
        }
    }
    my_list <- list("steps" = length(walk), "won" = win, "walk" = walk)
    return(my_list)
}
GamblingWalk<-function(init_money=50, prob=1/2, min_state=0, max_state=100, outcomes = c(-1,1), sim=100){
    "
    this is is the main function that performs the simulation.
    Take into account the taking variables, where the outcomes are the possible results of loosing or earning money
    the min and max state respresent the values where the simulation would stop.

    the outputs are sent as a list so the $ comand can be used.
    "
    results = c()
    for(i in 0:sim){
        res = randomWalk(init_money, prob, min_state, max_state, outcomes)
        results = c(results,res$won)
    }
    my_list <- list("results" = sum(results)/length(results), "walk" = res$walk)
    return(my_list)

}

```

```R
walkSim = GamblingWalk(60, sim=1000)
print(walkSim$results)
qplot(1:length(walkSim$walk), walkSim$walk, geom="line", xlab="Step", ylab="Money")
```

```R
walkSim = GamblingWalk(60, prob=0.51, sim=1000)
print(walkSim$results)
qplot(1:length(walkSim$walk), walkSim$walk, geom="line", xlab="Step", ylab="Money")
```

## 1.36
Recreate tge Reed-Frost model. Observe the effect on the course of the desease by changing the initial values for the number of people suceptible and infected. How does increasing the number of infected people affect the duration of the disease?

### Answer

```R
ReedFrost<-function(initial_healthy=400, initial_infected=3, probability_inf=0.004, sim=1000, steps=30){
    "
    This function perform a reedfrost model for infenction evolution over time.
    "
    time = c()
    for(i in 0:sim){
        counter = 0
        sus = initial_healthy
        inf = initial_infected
        healthy = c(sus)
        infected = c(inf)
        while(sus > 0 && counter<steps){
            counter=counter+1
            inf = rbinom(1,sus,1-(1-probability_inf)**inf)
            sus = sus-inf
            healthy = c(healthy, sus)
            infected = c(infected, inf)
        time = c(time,length(healthy))
        }
    }
    return(list("mean" = mean(time), "infected" = infected, "healthy" = healthy))
}
```

```R
reed = ReedFrost(steps = 25)
qplot(1:length(reed$infected), reed$infected, geom="line", xlab="Time", ylab="Infected")
```

```R
reed = ReedFrost(initial_infected = 20, steps = 25)
qplot(1:length(reed$infected), reed$infected, geom="line", xlab="Time", ylab="Infected")
```

## 1.37
Simulate the results of exercise 1.28 Estimate the mean and variance of the number of accidents per day.

### Answer
for Doing this remember exercise 1.28 had a poisson distribution with parameter $\Lambda$, which is a random variable uniformly distributed $(0,3)$

```R
AccidentSim<-function(min_uniform=0, max_uniform=3, sim=1000){
    "
    Function to perform the simulation from exercise 28, first simulate for getting lambda, then simulate
    with thar result.
    "
    results = c()
    for(i in 0:sim){
        lam = runif(1,min_uniform, max_uniform)
        results = c(results, rpois(1, lam))
    }
    return(list("mean" = mean(results), "var" = var(results), "results" = results))
}
```

```R
act = AccidentSim(sim=10000)
print(c(act$mean, act$var))
```

    [1] 1.512049 2.251680


# Tracing magnetic field lines

```python
# Import notebook dependencies
import os
import sys
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

from src import sha, IGRF13_FILE
```

## Tracing magnetic field lines

The basic principle in following a field line at a point is to calculate the field vector there, then 'take a step' in the direction of the field to a new point, and repeat. The smaller the step, the higher the accuracy. This can be expressed as

$$
\begin{align}
\delta \mathbf{r} &= k \mathbf{B}\\
&=\left(\delta r, \thinspace r \delta \theta, \thinspace r\sin{\theta}\delta \phi\right) = k\left(B_r, \thinspace B_\theta, \thinspace B_\phi\right)
\end{align}
$$

with $k$ a constant to scale the step size. We will use this to investigate the path of field lines computed using the IGRF, but first explore the simpler case of axisymmetric fields, where an explicit formula can be derived, avoiding the need for the 'stepping strategy'.

### Axisymmetric multipole field lines

In this case there is no variation with longitude ($\phi$), so that

$$\delta \mathbf{r}=\left(\delta r, r \delta \theta\right) = k(B_r, B_\theta)$$

using spherical coordinates, which gives the equation of the field line as

$$\frac{1}{r}\frac{dr}{d_\theta}= \frac{B_r}{B_\theta}.$$

This equation can be solved when the field is expressed in terms of spherical harmonics for the axisymmetric terms with $m=0$. In a spherical harmonic expansion, the zonal terms $P_n^o$ correspond to axial multipoles, ($P_1^0$ is a dipole, $P_2^0$ is a quadrupole, $P_3^0$ is an octupole and $P_4^0$ is an hexadecapole.) The field line equation for $P_n^0$ is (Willis and Young, 1987; Jeffreys, 1988)

$$r^n=\sin{\theta} \thinspace P_n^1(\cos{\theta}).$$

Note the power of spherical harmonic degree $n$, and that the Associated Legendre polynomial in the equation has $m=1$ not $m=0$.

For the field line that passes through a given starting position ($r_0$, $\theta_0$),

$$r_0^n = k P_n^1(\cos(\theta_0)) \sin(\theta_0),$$

which gives the following expression for the scaling factor,

$$k = \frac{r_0^n}{P_n^1(\cos(\theta_0)) \sin(\theta_0)}$$

is derived.

The (un-normalised) forms of $P_n^1$ for $n=1, \dots 4$ are

$$
\begin{align}
P_1^1&=\sin^2{\theta}\\
P_2^1&=\cos{\theta}\sin^2{\theta}\\
P_3^1&=(5\cos^2{\theta}-1)\sin^2{\theta}\\
P_4^1&=(7\cos^3{\theta}-3\cos{\theta})\sin^2{\theta}
\end{align}
$$

These, along with the expression for $k$, are used in the functions below to compute values of $r^n$ for $n=1, \dots 4$.

```python
def dipole(r0, theta_0, th):      # P(n=1, m=0)
    k = r0/(np.sin(theta_0)**2)
    return (k*np.sin(th)**2)

def quadpole(r0, theta_0, th):    # P(n=2, m=0)
    k = r0**2/(np.sin(theta_0)**2*np.cos(theta_0))
    P = np.cos(th)*np.sin(th)**2
    return (np.sqrt(np.abs(k)*np.abs(P)))

def octpole(r0, theta_0, th):     # P(n=3, m=0)
    k = r0**3/(np.sin(theta_0)**2*(5*np.cos(theta_0)**2-1))
    P = (5*np.cos(th)**2-1)*np.sin(th)**2
    return ((np.abs(k)*np.abs(P))**(1/3))

def hexdpole(r0, theta_0, th):    # P(n=4, m=0)
    k = r0**4/((7*np.cos(theta_0)**3-3*np.cos(theta_0))*np.sin(theta_0)**2)
    P =(7*np.cos(th)**3-3*np.cos(th))*np.sin(th)**2
    return ((np.abs(k)*np.abs(P))**(1/4))
```

You can plot dipole, quadrupole, octupole and hexadecapole field lines using the code below. Start by choosing the type of multipole field lines to plot by setting the value of **pole_type** to the desired spherical harmonic degree $n$, where $n=1, \dots 4$, and also setting some values of starting colatitude $\theta_0$ in the list **theta**. The program will draw the field lines passing through the point on the Earth's surface at each value of colatitude (represented as radius=1 so the axes are scaled in Earth radii). Different choices of colatitudes may be more illuminating in different cases.

### >> USER INPUT HERE: Set the plotting parameters below (pole type and starting colatitudes)

**pole_type** options:

1 = dipole

2 = quadrupole

3 = octupole

4 = hexadecapole

```python
# Pole type
pole_type = 2

# Experiment by changing a list of starting colatitudes
theta   = [5, 10, 15, 20, 30, 40]
```

Now create the plot.

```python
d2r     = np.deg2rad
axpoles = {1:dipole, 2:quadpole, 3:octpole, 4:hexdpole}
names   = {1:'Dipole', 2:'Quadrupole', 3:'Octupole', 4:'Hexadecapole'}
clines  = ['red', 'blue', 'grey', 'purple', 'brown', 'purple', 'pink', 'orange', 'magenta', 'olive', 'cyan']

# Define the "Earth"
r0  = 1
the = d2r(np.linspace(0, 360, 1000))
xe  = r0*np.sin(the)
ye  = r0*np.cos(the)

fig, ax = plt.subplots(figsize=(15, 15))
ax.set_aspect('equal')
ax.fill(xe, ye, color='lightgrey') # Plot the Earth

ic = -1
for i in theta:
    ic += 1
    theta_0 = d2r(i)
    th = np.linspace(theta_0, d2r(90), 1000)
    rad = axpoles[pole_type](r0, theta_0, th)
    xb = rad*np.sin(th)
    yb = rad*np.cos(th)
    xb[np.where(rad<1)] = np.nan
    yb[np.where(rad<1)] = np.nan
    ax.plot(xb, yb, color = clines[ic%10])
    # Assume a symmetrical distribution in the four quadrants
    ax.plot( xb, -yb, color = clines[ic%10])
    ax.plot(-xb,  yb, color = clines[ic%10])
    ax.plot(-xb, -yb, color = clines[ic%10])
ax.set_xlabel('Earth radii', fontsize=16)
ax.set_ylabel('Earth radii', fontsize=16)
ax.set_title(names[pole_type]+' field lines', fontsize=24);
```

### References

Jeffreys, B. (1988) ‘Derivations of the equation for the field lines of an axisymmetric multipole’, Geophysical Journal International. John Wiley & Sons, Ltd (10.1111), 92(2), pp. 355–356. doi: 10.1111/j.1365-246X.1988.tb01148.x.

Willis, D. M. and Young, L. R. (1987) ‘Equation for the field lines of an axisymmetric magnetic multipole’, Geophysical Journal International. Oxford University Press, 89(3), pp. 1011–1022. doi: 10.1111/j.1365-246X.1987.tb05206.x.

## Using the IGRF to trace field lines  (and find conjugate points)

The IGRF gives a fuller 3-D representation of the geomagnetic field, rather than the axisymmetric 2-D representation used above, and so the 'stepping strategy' is needed to follow field lines. The dipole field dominates, as we would expect, but it is interesting to to see how the colatitude and longitude change along the path. The starting and ending points at the Earth's surface are 'connected' by a field line - they are conjugate points. (The IGRF for 2020.0 is used in the code below.)

### >> >> USER INPUT HERE: Set the input parameters (listed below)

 1. Starting colatitude (degrees)
 2. Starting longitude (degrees)
 3. Step size (km)

```python
# Starting colatitude and longitude (east) in degrees
theta_0   = 30
phi_0     = 125

# Step size for the field line tracing in km
step_size = 10
```

Now do the calculation and plot the results. *** This may take a few seconds - be patient! ***

```python
d2r = np.deg2rad
r2d = np.rad2deg
fcalc = lambda x: np.sqrt(np.dot(x,x))

igrf13 = pd.read_csv(IGRF13_FILE, delim_whitespace=True, header=3)
gh2020 = np.append(0., igrf13['2020.0'])
NMAX   = 13

# Initialise variables at starting point
r0      = 6371.2
thrd    = d2r(theta_0)
phrd    = d2r(phi_0)
track   = [(r0, thrd, phrd)]  # Store coordinates of points on the field line
bxyz    = sha.shm_calculator(gh2020, NMAX, r0, theta_0, phi_0, 'Geocentric')
eff     = fcalc(bxyz)
lamb    = step_size/eff
rad     = r0

# Allow a maximum number of steps in the iteration for the field line to
# return to the Earth's surface
maxstep = 10000
newrad  = r0+0.001

step    = 0
while step <= maxstep and newrad >= r0:
    rad, th, ph  =  track[step]
    lx,  ly, lz  =  tuple(el*lamb for el in bxyz)
    newrad = rad+lz
    newth  = th+lx/rad
    newph  = ph-ly/(rad*np.sin(th))
    track += [(newrad, newth, newph)]
    bxyz   =  sha.shm_calculator(gh2020, NMAX, newrad, r2d(newth), \
                                 r2d(newph),'Geocentric')
    lamb   =  step_size/fcalc(bxyz)
    step  += 1

rads = np.array([r[0] for r in track])
ths  = np.array([t[1] for t in track])
phs  = np.array([p[2] for p in track])

fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, ncols=1, figsize=(10, 12))
ax0.plot(rads, r2d(ths))
ax0.invert_yaxis()
ax0.set_xlabel('Radial distance (km)', fontsize=14)
ax0.set_ylabel('Colatitude (degrees)', fontsize=14)
ax0.set_title('Field line tracing using the IGRF', fontsize=20)
ax1.plot(rads, r2d(phs), color='red')
ax1.set_xlabel('Radial distance (km)', fontsize=14)
ax1.set_ylabel('Longitude (degrees)', fontsize=14)
ax2.plot(r2d(phs), r2d(ths), color='green')
ax2.set_xlabel('Longitude (degrees)', fontsize=14)
ax2.set_ylabel('Colatitude (degrees)', fontsize=14)
ax2.invert_yaxis()
fig.tight_layout()
```

```python
print('\nCoordinates of the starting point:')
print('\tColatitude :', '{0:.1f}'.format(theta_0), 'degrees')
print('\tLongitude  :', '{0:.1f}'.format(phi_0), 'degrees')
print('\nCoordinates of the conjugate point:')
print('\tColatitude :', '{0:.1f}'.format(r2d(newth)), 'degrees')
print('\tLongitude  :', '{0:.1f}'.format(r2d(newph)%360), 'degrees')
```


# Forced convection of a bank of tubes in cross flow

Pressurized water is often available at elevated temperatures and may be used for space heating
or industrial process applications. In such cases it is customary to use a tube bundle in which
the water is passed through the tubes, while air is passed in cross flow over the tubes. Consider
 a staggered arrangement for which the tube outside diameter is 16.4 mm and the longitudinal and
 transverse pitches are SL = 34.3 mm and ST = 31.3 mm. There are seven rows of tubes in the
 airflow direction and eight tubes per row. Under typical operating conditions the cylinder
 surface temperature is at 70oC, while the air upstream temperature and velocity are 15oC and
 6m/s, respectively. Determine the air-side convection coefficient and the rate of heat transfer
 for the tube bundle.

## Known

```python
import numpy as np

from thermal_system_calculation import FluidState, Fluid

diameter_tube = 16.4e-3 # m
vertical_spacing = 31.3e-3 # m
horizontal_spacing = 34.3e-3 #m
temp_surface = 70 # degC
temp_air_in = 15 # degC
velocity_air = 6 # m/s
```

## Find

```python
h_air = None # convection coefficient
q = None # Heat transfer for the tube bundle
```

## Schematic

## Assumptions
1. Steady-state, incompressible flow conditions
2. Radiation neglected
3. Change in properties of air due to the temperature drop over the tube is neglected.

## Properties

```python
fluid_state_air_mean = FluidState(
    fluid=Fluid.AIR,
    temp_k=np.mean([temp_surface, temp_air_in]) + 273.15,
    characteristic_length=diameter_tube
)
print("The properties of air at the mean temperature are:")
print(f"\t- density: {fluid_state_air_mean.density:.3f} kg/m3")
print(f"\t- thermal conductivity: {fluid_state_air_mean.k:.3f} W/mK")
print(f"\t- dynamic viscosity: {fluid_state_air_mean.dynamic_viscosity:.3e} Pa s")
print(f"\t- specific heat capacity: {fluid_state_air_mean.cp} J/kgK")

fluid_state_air_surface = FluidState(
    fluid=Fluid.AIR,
    temp_k=temp_surface + 273.15,
    characteristic_length=diameter_tube
)
print("The properties of air at the surface temperature are:")
print(f"\t- density: {fluid_state_air_surface.density:.3f} kg/m3")
print(f"\t- thermal conductivity: {fluid_state_air_surface.k:.3f} W/mK")
print(f"\t- dynamic viscosity: {fluid_state_air_surface.dynamic_viscosity:.3e} Pa s")
print(f"\t- specific heat capacity: {fluid_state_air_surface.cp} J/kgK")
```

    The properties of air at the mean temperature are:
    	- density: 1.118 kg/m3
    	- thermal conductivity: 0.028 W/mK
    	- dynamic viscosity: 1.928e-05 Pa s
    	- specific heat capacity: 1007.0404483923909 J/kgK
    The properties of air at the surface temperature are:
    	- density: 1.029 kg/m3
    	- thermal conductivity: 0.030 W/mK
    	- dynamic viscosity: 2.056e-05 Pa s
    	- specific heat capacity: 1008.6990251132372 J/kgK

## Analysis
We will find the convection from the definition of Nusselt number and the empirical formula for
finding the Nusselt number.
$$
\begin{align}
    Nu &= \frac{hL}{k_f} \\
    \overline{Nu}_D &= C_1 C_2 Re_{D, mathrm{max}}^m Pr^{0.36} \left(\frac{Pr}{Pr_s}\right)^{1/4}
\end{align}
$$

We will first check the flow conditions.

```python
# First we need to check where the maximum velocity happens.
diagonal_spacing = np.sqrt(horizontal_spacing**2 + (0.5 * vertical_spacing)**2)
if 2 * (diagonal_spacing - diameter_tube) < (vertical_spacing - diameter_tube):
    velocity_max = vertical_spacing / (2 * (diagonal_spacing - diameter_tube)) * velocity_air
    print(f"Maximum velocity at diagonal path: {velocity_max} m/s")
else:
    velocity_max = vertical_spacing / (vertical_spacing - diameter_tube) * velocity_air
    print(f"Maximum velocity at diagonal path: {velocity_max} m/s")

reynolds_number = fluid_state_air_mean.density * fluid_state_air_mean.characteristic_length * \
                  velocity_max / fluid_state_air_mean.dynamic_viscosity
print(f"Reynolds number is {reynolds_number}.")
```

    Maximum velocity at diagonal path: 12.604026845637584 m/s
    Reynolds number is 11989.6620878666.

From the constant table and correction factor table below:

```python
# Calculate h
print(f"The ratio between the vertical spacing and horizontal spacing is {vertical_spacing / horizontal_spacing}.")
c1 = 0.35 * (vertical_spacing / horizontal_spacing)**0.2
m = 0.6
c2 = 0.95
prandtdl_number = fluid_state_air_mean.cp * fluid_state_air_mean.dynamic_viscosity / fluid_state_air_mean.k
prandtdl_number_surface = fluid_state_air_mean.cp * fluid_state_air_mean.dynamic_viscosity / fluid_state_air_mean.k
Nu = c1 * c2 * reynolds_number**m * prandtdl_number**0.36 * (prandtdl_number / prandtdl_number_surface)**0.25
h = Nu * fluid_state_air_mean.k / diameter_tube

print(f"Convection coefficient is {h} W/m2K")
```

    The ratio between the vertical spacing and horizontal spacing is 0.9125364431486882.
    Convection coefficient is 135.39179091826264 W/m2K

For calculation of heat transfer rate, we need to use the logarithmic mean temperature difference instead of arithmic temperature difference. In addition, we need to estimate the outlet temperature of the stream that externally passes the tubes. We can do that by
$$\Delta T_m = \frac{(T_s - T_i) - (T_s - T_o)}{\ln\left(\frac{T_s - T_i}{T_s - T_o}\right)}$$
$$\frac{T_s - T_o}{T_s - T_i} = exp\left(-\frac{\pi D N \overline{h}}{\rho V N_T S_T c_p}\right)$$

```python
# Calculate q
def get_log_mean_temp_difference(
    temp_surface: float,
    temp_in: float,
    temp_out: float
) -> float:
    return (temp_out - temp_in) / np.log((temp_surface - temp_in) / (temp_surface - temp_out))

def get_temp_out_estimation(
    temp_surface: float,
    temp_in: float,
    diameter_tube: float,
    number_tube: float,
    vertical_spacing: float,
    number_tube_per_row: float,
    velocity: float,
    h: float,
    fluid_state: FluidState
) -> float:
    return temp_surface - (temp_surface - temp_in) * \
        np.exp(-(np.pi * diameter_tube * number_tube * h) /
               (fluid_state.density * velocity * number_tube_per_row * vertical_spacing * fluid_state.cp))

def get_heat_transfer_tube_bank(
    number_tube: float,
    h: float,
    diameter_tube: float,
    log_mean_temp_diff: float,
    length: float
) -> float:
    return number_tube * h * np.pi * diameter_tube * log_mean_temp_diff * length

number_tube_per_row = 8
number_row = 7
number_tube = number_tube_per_row * number_row

fluid_state_air_in = FluidState(
    fluid=Fluid.AIR,
    temp_k=temp_air_in + 273.15,
    characteristic_length=diameter_tube
)

temp_out = get_temp_out_estimation(
    temp_surface=temp_surface,
    temp_in=temp_air_in,
    diameter_tube=diameter_tube,
    number_tube=number_tube,
    number_tube_per_row=number_tube_per_row,
    vertical_spacing=vertical_spacing,
    velocity=velocity_air,
    h=h,
    fluid_state=fluid_state_air_in
)

log_mean_temp_diff = get_log_mean_temp_difference(
    temp_surface=temp_surface,
    temp_in=temp_air_in,
    temp_out=temp_out
)

q = get_heat_transfer_tube_bank(
    number_tube=number_tube,
    h=h,
    diameter_tube=diameter_tube,
    log_mean_temp_diff=log_mean_temp_diff,
    length=1
)

print(f'Heat transfer rate is {q:.3f} W/m')
```

    Heat transfer rate is 19370.745 W/m

```python

```


```python
from sympy import *
from sympy import abc
from IPython.display import display, Math, Latex
from sympy.interactive import printing
printing.init_printing(use_latex=True)
```

## Optimal Control Problem
Minimize $$\int_0^Tf(t,x,u)~dt$$ subject to
$$
\begin{cases}
x'(t) = b(t,x,u)\\
x(0) = x_0
\end{cases}
$$

```python
t, x, u= symbols('t x u')
Vt, Vx = symbols('V_t V_x')
f = x + 0.5 * u**2
b = x + u
```

## Hamilton-Jacobi-Bellman Equation

```python
hjbeq = r'\frac{\partial V}{\partial t} + \min_u \left[' + latex(f) + r'+\frac{\partial V}{\partial x}\left(' + latex(b)+ r'\right)\right] = 0'
display(Math(hjbeq))

hjbbd = r'V(T,x)=0'
display(Math(hjbbd))
```

$$\frac{\partial V}{\partial t} + \min_u \left[u^{2}+\frac{\partial V}{\partial x}\left(u\right)\right] = 0$$

$$V(T,x)=0$$

## Successive Approximation

```python
sahjbeq = r'\frac{\partial V_k}{\partial t} + ' + latex(f) + r'+\frac{\partial V_k}{\partial x} \left(' + latex(b) + r'\right)= 0'
display(Math(sahjbeq.replace('u', 'u_k')))

sahjbbd = r'V_k(T,x)=0'
display(Math(sahjbbd))
```

$$\frac{\partial V_k}{\partial t} + u_k^{2}+\frac{\partial V_k}{\partial x} \left(u_k\right)= 0$$

$$V_k(T,x)=0$$

```python
update = r'u_{k+1} = \arg\min_{u_k}\left[' + latex(f) + r'+\frac{\partial V_k}{\partial x} \left(' + latex(b) + r'\right)\right]'
display(Math(update))
```

$$u_{k+1} = \arg\min_{u_k}\left[u^{2}+\frac{\partial V_k}{\partial x} \left(u\right)\right]$$

```python

```


# 第二部分：分类问题（Classification）和逻辑回归（Logistic regression）

在前面的回归问题中，我们尝试预测的变量$y$是连续变量，现在我们来讨论分类问题。分类问题与回归问题不同之处在于，$y$的取值是少量的离散值。现在，我们先介绍**二元分类（binary classification）**，也就是$y$只能取$0$或$1$。（在二元分类中介绍的很多方法也可以推广至多元情况。）比如，我们尝试实现一个垃圾邮件分类器，用$x^{(i)}$表示邮件的某些特征，通过分类算法预测得到：如果该邮件是垃圾邮件时$y=1$，反之$y=0$。我们也把$0$称为**negative class**，把$1$称为**positive class**，我们有时也使用$-,\ +$作为标记。对于给定的$x^{(i)}$，$y^{(i)}$也称作训练样本的**label**。

## 5. 逻辑回归

对于分类问题，我们也可以无视$y$是取离散值的特征而继续使用线性回归，使用老办法通过$x$预测$y$的取值。然而，强行使用线性回归的方法处理分类问题通常会得到很糟糕的预测结果。况且，在我们已经明确知道$y\in\{0,\ 1\}$的情况下仍旧使用线性回归的$h_\theta(x)\in\mathbb{R}$是不合逻辑的。

所以，我们应该对原来的假设函数做出修改：

$$h_\theta(x)=g\left(\theta^Tx\right)=\frac{1}{1+e^{-\theta^Tx}}$$

这里的$g(z)=\frac{1}{1+e^{-z}}$，也叫作**逻辑函数（logistic function）**或**S型函数（sigmoid function）**，图像如下图所示：

在$z\to\infty$时函数$g(z)$趋近于$1$，而$z\to-\infty$是函数$g(z)$趋近于$0$。而且不光是$g(z)$，包括上面的$h(x)$都在$(0, 1)$之间取值。有$\theta^Tx=\theta_0+\displaystyle\sum_{j=0}^n\theta_jx_j$，这里我们依然令$x_0=1$。

其它的在$(-\infty,\ \infty)$上能够从$0$取到$1$的可导的连续函数也可以使用，但是因为一些原因（我们将在后面的关于GLM、生成学习法的课程中了解），选择上面的逻辑函数是一种“自然”的结果。再继续前，我们先来看一下逻辑函数求导的优良性质：

$$\begin{align}g'(z)&=\frac{\mathrm d}{\mathrm dz}\frac{1}{1+e^{-z}}\\&=\frac{1}{\left(1+e^{-z}\right)^2}\left(e^{-z}\right)\\&=\frac{1}{1+e^{-z}}\cdot\left(1-\frac{1}{1+e^{-z}}\right)\\&=g(z)(1-g(z))\end{align}$$

接下来，我们应该如何用$\theta$拟合逻辑回归模型呢？在线性回归中我们知道了，从一些列假设下的最大似然估计可以推导出最小二乘回归的合理性，所以，我们也可以按照这个思路，给我们的分类模型赋予一些列概率假设，然后通过最大似然估计拟合参数。假设：

$$\begin{align}P(y=1\mid x;\theta)&=h_\theta(x)\\P(y=0\mid x;\theta)&=1-h_\theta(x)\end{align}$$

当然，我们也可以将这两个假设合入一个简洁的式子：

$$p(y\mid x;\theta)=(h_\theta(x))^y(1-h_\theta(x))^{(1-y)}$$

假设训练集中的$m$个训练样本是相互独立的，我们就可以这样写出参数的似然估计：

$$\begin{align}L(\theta)&=p\left(\vec y\mid X;\theta\right)\\&=\prod_{i=1}^m p\left(y^{(i)}\mid x^{(i)};\theta\right)\\&=\prod_{i=1}^m\left(h_\theta\left(x^{(i)}\right)\right)^{y^{(i)}}\left(1-h_\theta\left(x^{(i)}\right)\right)^{1-y^{(i)}}\end{align}$$

跟线性回归中的运算一样，我们取对数便于求导：

$$\begin{align}\mathscr{l}(\theta)&=\log L(\theta)\\&=\sum_{i=1}^my^{(i)}\log h_\theta\left(x^{(i)}\right)+\left(1-y^{(i)}\right)\log\left(1-h_\theta\left(x^{(i)}\right)\right)\end{align}$$

我们依然沿用线性回归中的思路，通过求导发现最大化似然函数的极值点，这次我们使用梯度上升法。我们使用矢量记法，则有更新规则为$\theta:=\theta+\alpha\nabla_\theta\mathscr{l}(\theta)$。（因为想要求函数的最大值，所以我们在更新规则里使用了加号。）我们现在假设训练集中只有一个训练样本$(x,\ y)$，对其求导，希望能导出适用于随机梯度上升更新规则：

$$\begin{align}\frac{\partial}{\partial\theta_j}\mathscr{l}(\theta)&=\left(y\frac{1}{g\left(\theta^Tx\right)}-(1-y)\frac{1}{1-g\left(\theta^Tx\right)}\right)\frac{\partial}{\partial\theta_j}g\left(\theta^Tx\right)\\&=\left(y\frac{1}{g\left(\theta^Tx\right)}-(1-y)\frac{1}{1-g\left(\theta^Tx\right)}\right)g\left(\theta^Tx\right)\left(1-g\left(\theta^Tx\right)\right)\frac{\partial}{\partial\theta_j}\theta^Tx\\&=\left(y\left(1-g\left(\theta^Tx\right)\right)-(1-y)g\left(\theta^Tx\right)\right)x_j\\&=(y-h_\theta(x))x_j\end{align}$$

在上面的求导中，从第一步到第二步我们使用了逻辑函数求导性质$g'(z)=g(z)(1-g(z))$。这样，我们也就得到了适用于随机梯度上升的更新规则：

$$\theta_j:=\theta_j+\alpha\left(y^{(i)}-h_\theta\left(x^{(i)}\right)\right)x_j$$

如果使用批量梯度上升则是：

$$\theta_j:=\theta_j+\alpha\sum_{i=1}^m\left(y^{(i)}-h_\theta\left(x^{(i)}\right)\right)x_j$$

回顾上一讲的最小均方法法，我们会发现上面这条更新规则和以前的更新规则一模一样，我们在这里必须说明，$h_\theta(x)$已经变为关于$\theta^Tx^{(i)}$的非线性函数，所以这并不是同一个算法。尽管如此，对于不同的算法得到同样形式的更新规则，我们还是感到很惊讶。对于这个现象，我们会在后面关于GLM模型的课程中给出解释。

## 6. 感知算法（perceptron learning algorithm）

最后，我们来简要的介绍一下感知算法，我们以后在介绍学习理论是还会继续讨论这个算法。对于上面的逻辑回归，我们如何“强迫”算法只输出$0$或$1$？我们会自然的想到改变$g$的定义，使其变为一个阈函数：

$$g(z)=\begin{cases}1\quad z\geq 0\\0\quad z\lt 0\end{cases}$$

如果我们依然令$h_\theta(x)=g\left(\theta^Tx\right)$，并将$g$修改为上面的阈函数，然后使用$\theta_j:=\theta_j+\alpha\displaystyle\sum_{i=1}^m\left(y^{(i)}-h_\theta\left(x^{(i)}\right)\right)x_j$或$\theta_j:=\theta_j+\alpha\left(y^{(i)}-h_\theta\left(x^{(i)}\right)\right)x_j$作为学习规则，那么我们就实现了一个**感知算法（perceptron learning algorithm）**。

在六十年代，这个感知算法被认为是一种粗略的描述脑中独立神经元工作方式的模型。该模型很简单，所以我们也将它作为日后讨论学习理论的起点。需要注意的是，尽管这个算法看起来并不特殊，但实际上，与其前面的逻辑回归、最小二乘线性回归比起来，这是一个非常不同的算法：比如，我们很难赋予它一个在概率上有意义的解释，也很难从最大似然估计推导出感知算法。


```python
from sympy import *
from sympy.plotting import *
init_printing()
#Vamos a definir la funcion modulo:
def modulo(vec):
    aux = 0
    for i in vec:
        aux+=S(i)**2
    return sqrt(aux)
#Vamos a definir la funcion para derivar:
def derivar(vec,var):
    aux = []
    for i in vec:
        aux.append(diff(S(i),var))
    return aux
#Vamos a definir la funcion para integrar:
def integrar(vec, var):
    aux = []
    for i in vec:
        aux.append(integrate(S(i),var))
    return aux
#Vamos a definir la funcion para sumar vectores:
def sumarvectores(vec1,vec2):
    return [sum(x) for x in zip(*[vec1,vec2])]
#Vamos a definir una funcion para hacer producto punto de vectores:
def productopunto(vec1, vec2):
    return (vec1[0]*vec2[0])+(vec1[1]*vec2[1])
```

Pregunta 1:

Una particula parte de la posicion r(0)=<3;-2;4>.

Determine los vectores a(t) y r(t) si su velocidad es v(t)=<4t;4t^3-2t;5>

```python
##Primero definimos las variables inciales:
t = symbols('t')
r0=[3,-2,4]
vt=[4*t,4*(t**3)-(2*t),5]
```

```python
#Para hallar la aceleracion, derivamos la velocidad con respecto al tiempo:
derivar(vt,t)
#a(t) =
```

```python
#Para hallar la posicion con respecto al tiempo, tenemos que integrar la velocidad con respecto al tiempo y sumar con la posicion inicial:
rt = integrar(vt,t)
sumarvectores(rt, r0)
#r(t) =
```

Pregunta 1(intento 2):

Una particula parte de la posicion r(0)=<1;-2;3>.

Determine los vectores a(t) y r(t) si su velocidad es v(t)=<6t;6t^5-2t;7>

```python
##Primero definimos las variables inciales:
t = symbols('t')
r0=[1,-2,3]
vt=[6*t,6*(t**5)-(2*t),7]
```

```python
#Para hallar la aceleracion, derivamos la velocidad con respecto al tiempo:
derivar(vt,t)
#a(t) =
```

```python
#Para hallar la posicion con respecto al tiempo, tenemos que integrar la velocidad con respecto al tiempo y sumar con la posicion inicial:
rt = integrar(vt,t)
sumarvectores(rt, r0)
#r(t) =
```

Pregunta 1(intento x):

Una particula parte de la posicion r(0)=<3;-6;9>.

Determine los vectores a(t) y r(t) si su velocidad es v(t)=<6t;6t^5-2t;7>

```python
##Primero definimos las variables inciales:
t = symbols('t')
#intento 3:
# r0=[3,-6,9]
# vt=[6*t,6*(t**5)-(2*t),7]
#intento 4:
r0=[1,-2,3]
vt=[8*t,8*(t**7)+4*t,9]

```

```python
#Para hallar la aceleracion, derivamos la velocidad con respecto al tiempo:
derivar(vt,t)
#a(t) =
```

```python
#Para hallar la posicion con respecto al tiempo, tenemos que integrar la velocidad con respecto al tiempo y sumar con la posicion inicial:
rt = integrar(vt,t)
sumarvectores(rt, r0)
#r(t) =
```

Pregunta 2:

Sea la curva C dada por la funcion vectorial:

r(t) = <raiz(2)*cos(t); sen(t); 2-sen(t)>, 0<=t<=pi/2

Calcule la longitud de la curva C

```python
##Definimos las variables iniciales:
t = symbols('t')
#intento 1:
# rt = [sqrt(2)*cos(t), sin(t), 2-sin(t)]
# lim = [0,pi/2]
#intento 4:
rt=[exp(t)-t,4*exp(t/2),2]
lim = [0,3]

```

```python
#Calculamos su rapidez dependiendo de la
rtdt = derivar(rt,t)
rapidez = modulo(rtdt)
rapidez
```

```python
#Para hallar la longitud planteamos la siguiente integral:
Integral(rapidez,(t,lim[0],lim[1]))
```

```python
#Resultado de la integral:
simplify(integrate(rapidez,(t,lim[0],lim[1])))
```

Pregunta 2(intento 2):

Sea la curva C dada por la funcion vectorial:

r(t) = <3t-t^3; 3t^2; 1>, 0<=t<=2

Calcule la longitud de la curva C

```python
##Definimos las variables iniciales:
t = symbols('t', real=True)
rt = [3*t-(t**3),3*(t**2),1]
lim = [0,2]
```

```python
rtdt = derivar(rt,t)
rapidez = simplify(modulo(derivar(rt,t)))
rapidez

```

```python
#Resultado de la integral:
simplify(integrate(rapidez, (t,lim[0],lim[1])))
```

Pregunta 2(intento 3):

Sea la curva C dada por la funcion vectorial:

r(t) = <raiz(2)*cos(t);sen(t);2-sen(t)>, 0<=t<=pi/2

Calcule la longitud de la curva C

```python
##Definimos las variables iniciales:
t = symbols('t', real=True)
rt = [sqrt(2)*cos(t),sin(t),2-sin(t)]
lim = [0,pi/2]
```

```python
rtdt = derivar(rt,t)
rapidez = simplify(modulo(derivar(rt,t)))
rapidez
#Para hallar la longitud necesitamos la rapidez
```

```python
#Para hallar la respuesta tenemos que integrar la rapidez con respecto al tiempo, considerando los limites de t:
simplify(integrate(rapidez, (t,lim[0],lim[1])))
```

PREGUNTA 3:

Dado el campo de fuerza:

F(x;y)=<xy;y^2>

y la curva C:

y^2-x=1

que va desde A(8;-3) hasta B(-1;0)

```python
x,y = symbols('x y')
curva = y**2-x-1
campo = [x*y,y**2]
puntoA = [8,-3]
puntoB = [-1,0]
```

```python
#Parametrizar la curva C, usando y=t:
rt = [solve(curva, x)[0].subs(y,t) , t]
rt
#Vector rt =
```

```python
solve(curva, x)[0]
```

```python
frt= [campo[0].subs(x,rt[0]).subs(y,rt[1]),campo[1].subs(y,rt[1])]
frt
#vector f(r(t))=
```

```python
dr = derivar(rt,t)
dr
#r'(t)=
```

```python
#Planteamos la integral que permite calcular el trabajo realizar por el campo de fuerza F a lo largo de la curva C desde A hasta B
#Como y=t, los limites de y son:
limsuperior = max(puntoA[1],puntoB[1])
liminferior = min(puntoA[1],puntoB[1])
Integral(simplify(productopunto(frt,dr)), (t,liminferior,limsuperior))
```

PREGUNTA 3 (Intento 2):

Dado el campo de fuerza:

F(x;y)=<y^2;xy>

y la curva C:

y^3-x=1

que va desde A(-2;-1) hasta B(0;1)

```python
x,y,t = symbols('x y t')
#Valores iniciales:
# curva = y**3-x-1
# campo = [y**2,x*y]
# puntoA = [-2,-1]
# puntoB = [0,1]

# curva = y**2-x-1
# campo = [x*y,y**2]
# puntoA = [-1,0]
# puntoB = [8,3]

curva = x**2-y+1
campo = [x*y,x**2]
puntoA = [0,1]
puntoB = [2,5]
```

```python
#Parametrizar la curva C, usando x=t:
rt = [t, solve(curva, y)[0].subs(x,t)]
rt
#Vector rt =
```

```python
#Limites de t:
limites = [min(puntoA[0],puntoB[0]),max(puntoA[0],puntoB[0])]
limites
```

```python
frt= [campo[0].subs(x,rt[0]).subs(y,rt[1]),campo[1].subs(y,rt[1]).subs(x,rt[0])]
frt
#vector f(r(t))=
```

```python
dr = derivar(rt,t)
dr
#r'(t)=
```

```python
#Planteamos la integral que permite calcular el trabajo realizar por el campo de fuerza F a lo largo de la curva C desde A hasta B
#Como y=t, los limites de y son:
limsuperior = max(puntoA[0],puntoB[0])
liminferior = min(puntoA[0],puntoB[0])
Integral(simplify(productopunto(frt,dr)), (t,liminferior,limsuperior))
```

PREGUNTA 3 (Intento 3):

Dado el campo de fuerza:

F(x;y)=<2(y^2);-xy>

y la curva C:

x^2-y=-1

que va desde A(0;1) hasta B(1;2)

```python
x,y,t = symbols('x y t')
#Valores iniciales:
curva = x**2-y+1
campo = [2*(y**2),-1*x*y]
puntoA = [0,1]
puntoB = [1,2]
```

```python
#Parametrizar la curva C, usando x=t:
rt = [t,solve(curva, y)[0].subs(x,t)]
rt
#Vector rt =
```

```python
#Limites de t:
limites = [min(puntoA[0],puntoB[0]),max(puntoA[0],puntoB[0])]
limites
```

```python
frt= [campo[0].subs(x,rt[0]).subs(y,rt[1]),campo[1].subs(y,rt[1]).subs(x,rt[0])]
frt
#vector f(r(t))=
```

```python
dr = derivar(rt,t)
dr
#r'(t)=
```

```python
#Planteamos la integral que permite calcular el trabajo realizar por el campo de fuerza F a lo largo de la curva C desde A hasta B
#Como y=t, los limites de y son:
limsuperior = max(puntoA[1],puntoB[1])
liminferior = min(puntoA[1],puntoB[1])
Integral(simplify(productopunto(frt,dr)), (t,liminferior,limsuperior))
```

PREGUNTA 4:

Aplique el Teorema Fundamental de Integrales de Línea para calcular el trabajo (W en joules) realizado por el campo de las fuerzas

F(x;y) = <5x^4y+y^2;x^5+2yx>
 sobre una partícula que se mueve a lo largo del segmento de recta desde A(0;1) hasta B(2;1)

```python
#definifmos las variables iniciales:
x,y = symbols('x y')
fxy = [5*(x**4)*y+y**2,x**5+2*x*y]
p = fxy[0]
q = fxy[1]
puntoA = [0,1]
puntoB = [2,1]
# puntoA = [-2,0]
# puntoB = [1,2]
```

```python
#El teorema dice: w = f(B) - f(A), donde F debe de ser conservativo, donde Qx = Py
#Primero tenemos que ver si es conservativo:
if diff(q,x)==diff(p,y):
    print('Es conservatorio')
else:
    print('No es conservatorio')
```

    Es conservatorio

```python
#Hallamos la funcion exponencial:
f = integrate(p,x)
f
```

```python
#Ahora que tenemos la funcion exponencial (f), hallamos el trabajo
wA = f.subs(x,puntoA[0]).subs(y,puntoA[1])
wB = f.subs(x,puntoB[0]).subs(y,puntoB[1])
#w =
abs(wA-wB)
```

PREGUNTA 4 (Intento 2):

Aplique el Teorema Fundamental de Integrales de Línea para calcular el trabajo (W en joules) realizado por el campo de las fuerzas

F(x;y) = <5(x^4)*y+y^2;x^5+2xy>

sobre una partícula que se mueve a lo largo del segmento de recta desde A(-2;0) hasta B(1;2)

```python
#definifmos las variables iniciales:
x,y = symbols('x y')
fxy = [5*(x**4)*y+y**2,x**5+2*x*y]
p = fxy[0]
q = fxy[1]
#Sólo cambian los puntos con respecto al anterior intento
puntoA = [-2,0]
puntoB = [1,2]
```

```python
#El teorema dice: w = f(B) - f(A), donde F debe de ser conservativo, donde Qx = Py
#Primero tenemos que ver si es conservativo:
if diff(q,x)==diff(p,y):
    print('Es conservatorio')
else:
    print('No es conservatorio')
```

    Es conservatorio

```python
#Hallamos la funcion exponencial:
f = integrate(p,x)
f
```

```python
#Ahora que tenemos la funcion exponencial (f), hallamos el trabajo
wA = f.subs(x,puntoA[0]).subs(y,puntoA[1])
wB = f.subs(x,puntoB[0]).subs(y,puntoB[1])
#w =
abs(wA-wB)
```

PREGUNTA 4 (Intento 3):

Aplique el Teorema Fundamental de Integrales de Línea para calcular el trabajo (W en joules) realizado por el campo de las fuerzas

F(x;y) = <3(x^2)y+y^4;x^3+4(y^3)x>

sobre una partícula que se mueve a lo largo del segmento de recta desde A(3;0) hasta B(1;2)

```python
#definifmos las variables iniciales:
x,y = symbols('x y')
fxy = [3*(x**2)*y+y**4,x**3+4*(y**3)*x]
p = fxy[0]
q = fxy[1]
puntoA = [3,0]
puntoB = [1,2]
```

```python
#El teorema dice: w = f(B) - f(A), donde F debe de ser conservativo, donde Qx = Py
#Primero tenemos que ver si es conservativo:
if diff(q,x)==diff(p,y):
    print('Es conservatorio')
else:
    print('No es conservatorio')
```

    Es conservatorio

```python
#Hallamos la funcion exponencial:
f = integrate(p,x)
f
```

```python
#Ahora que tenemos la funcion exponencial (f), hallamos el trabajo
wA = f.subs(x,puntoA[0]).subs(y,puntoA[1])
wB = f.subs(x,puntoB[0]).subs(y,puntoB[1])
#w =
abs(wA-wB)
```

PREGUNTA 5:

Use el Teorema de Green, para calcular el flujo del campo vectorial a trvés de la curva cerrada C, con orientacion positiva.

simbolo green = lc

lc (x^2020+y)dx + (e^(y^2)+1-x)dy

donde C:

x^2+y^2=16

Hallar la integral doble que permite el cálculo del flujo y el valor de la misma.

```python
x,y = symbols('x y')
curva = x**2+y**2-16 #de lo cual se sabe que el radio es 4
funcion = [x**2020+y,exp(y**2)+1-x]
#la parte de dx pasa a ser el primer factor y dy pasa a ser el segundo
#funcion = P(x,y) + Q(x,y)
```

```python
#Grafico de la curva
aux = solve(curva,y)
p1 = plot(aux[0], show=False)
p2 = plot(aux[1], show=False)
p1.append(p2[0])
p1.show()
```

```python
#hallamos los limites de la funcion:
limites = [Limit(aux[0],x,0).doit(),Limit(aux[1],x,0).doit()]
limites
```

```python
#integral planteada:
intbase = Integral(S(diff(funcion[1],x)-diff(funcion[0],y)), (y,aux[0], aux[1]))
Integral(intbase, (x, min(limites), max(limites)))
```

```python
#Resultado
Integral(intbase, (x, min(limites), max(limites))).doit()
```

PREGUNTA 5(Intento 2):

Use el Teorema de Green, para calcular el flujo del campo vectorial a trvés de la curva cerrada C, con orientacion positiva.

simbolo green = lc

lc (sin(x^2)+y)dx + (e^(y^2020+1)-x)dy

donde C:

x^2+y^2=25

Hallar la integral doble que permite el cálculo del flujo y el valor de la misma.

```python
x,y = symbols('x y')
curva = x**2+y**2-25 #de lo cual se sabe que el radio es 4
funcion = [sin(x**2)+y,exp(y**2+1)-x]
#la parte de dx pasa a ser el primer factor y dy pasa a ser el segundo
#funcion = P(x,y) + Q(x,y)
```

```python
#Grafico de la curva
aux = solve(curva,y)
p1 = plot(aux[0], show=False)
p2 = plot(aux[1], show=False)
p1.append(p2[0])
p1.show()
```

```python
#hallamos los limites de la funcion:
limites = [Limit(aux[0],x,0).doit(),Limit(aux[1],x,0).doit()]
limites
```

```python
#integral planteada:
intbase = Integral(S(diff(funcion[1],x)-diff(funcion[0],y)), (y,aux[0], aux[1]))
integralplan = Integral(intbase, (x, min(limites), max(limites)))
integralplan
```

```python
#Resultado
integralplan.doit()
```

PREGUNTA 5(Intento 3):

Use el Teorema de Green, para calcular el flujo del campo vectorial a trvés de la curva cerrada C, con orientacion positiva.

simbolo green = lc

lc (sin(x^2)+y)dx + (e^(y^2020+1)-x)dy

donde C:

x^2+y^2=1

Hallar la integral doble que permite el cálculo del flujo y el valor de la misma.

```python
x,y = symbols('x y')
curva = x**2+y**2-16 #de lo cual se sabe que el radio es 1
funcion = [sin(x**2)+y,exp(y**2020+1)-x]
#la parte de dx pasa a ser el primer factor y dy pasa a ser el segundo
#funcion = P(x,y) + Q(x,y)
```

```python
#Grafico de la curva
aux = solve(curva,y)
p1 = plot(aux[0], show=False)
p2 = plot(aux[1], show=False)
p1.append(p2[0])
p1.show()
```

```python
#hallamos los limites de la funcion:
limites = [Limit(aux[0],x,0).doit(),Limit(aux[1],x,0).doit()]
limites
```

```python
#integral planteada:
intbase = Integral(S(diff(funcion[1],x)-diff(funcion[0],y)), (y,aux[0], aux[1]))
integralplan = Integral(intbase, (x, min(limites), max(limites)))
integralplan
```

```python
#Resultado
integralplan.doit()
```


# Solution {-}
Factor spectral density function $S(s)$:

```python
from sympy import symbols, factor

s, sigma, beta, K = symbols('s sigma beta K')

Sx = 1/(s + K)*1/(-s + K)*(2*sigma**2*beta)/(-s**2 + beta**2)
Sx
```

$\displaystyle \frac{2 \beta \sigma^{2}}{\left(K - s\right) \left(K + s\right) \left(\beta^{2} - s^{2}\right)}$

```python
factor(Sx, s)
```

$\displaystyle \frac{2 \beta \sigma^{2}}{\left(- K + s\right) \left(K + s\right) \left(- \beta + s\right) \left(\beta + s\right)}$


```python
import math, cmath
from sympy import *
theta = symbols('theta')
init_printing(use_unicode=True)

def P_l(l):
    """Legendre Polynomial"""
    fx = cos(theta)
    x = cos(theta)
    factor = 1/(2**l * math.factorial(l))
    fx = diff((fx**2 - 1)**l, x, l)
    P_l = factor * fx
    return P_l # SymPy Expression!

def Pm_l(m, l):
    """Associated Legendre Function"""
    fx = cos(theta)
    x = cos(theta)
    factor = (1-fx**2)**(m/2)
    fx = diff(P_l(l), x, abs(m))
    Pm_l = factor * fx
    return Pm_l # SymPy Expression!

def angular_wave_func(m, l, theta_value, phi):
    if m > 0:
        E = (-1)**m
    else:
        E = 1
    A_factor_1 = (2*l+1)/(4*math.pi)
    A_factor_2 = math.factorial(l-abs(m)) / math.factorial(l+abs(m))
    A = math.sqrt(A_factor_1*A_factor_2)
    B = cmath.exp(m*phi*1j)
    C = Pm_l(m,l).subs(theta, theta_value)
    long_ans = complex(E * A * B * C.evalf())
    ans = round(long_ans.real, 5) + round(long_ans.imag, 5)*1j
    return ans
```

```python
print(angular_wave_func (0,0,0,0))
print(angular_wave_func (0,1,cmath.pi ,0))
print(angular_wave_func (1,1,cmath.pi/2,cmath.pi))
print(angular_wave_func (0,2,cmath.pi ,0))
```

    (0.28209+0j)
    (-0.4886+0j)
    (0.34549+0j)
    (0.63078+0j)

```python
import math, cmath
from sympy import *
import scipy.constants as c
e = 2.7182818284590452353602874713526624977572
r, x, a = symbols('r x a')
init_printing(use_unicode=True)

def Lq(q,n):
    """Laguerre Polynomial"""
    factor = e**x
    expression = e**(-x) * x**q
    A = diff(expression, x, q)
    return factor * A

def assoc_Lq(p,q,n):
    """Associated Laguerre Function"""
    factor = (-1)**p
    A = diff(Lq(q,n), x, p)
    assoc_Lq = (factor * A).subs(x,2*r/(n*a))
    return assoc_Lq

def radial_wave_func(n, l, radius):
    bohr=c.physical_constants['Bohr radius'][0]
    A_factor_1 = (2/(n*a))**3
    A_factor_2 = (math.factorial(n-l-1)) / (2*n*(math.factorial(n+l))**3)
    A = (A_factor_1 * A_factor_2)**0.5
    B = e**(-radius/(n*a))
    C = ((2*radius)/(n*a))**l
    p = 2*l + 1
    q = n-l-1+p
    D = assoc_Lq(p,q,n)
    expression = A * B * C * D / (a**(-3/2))
    subbed = expression.subs(r, radius).subs(a, bohr)
    return round(subbed.evalf(), 5)
```

```python
print(radial_wave_func (1,0,a))
print(radial_wave_func (2,1,a))
print(radial_wave_func (2,1,2*a))
print(radial_wave_func (3,1,2*a))
```

    0.73576
    0.12381
    0.15019
    0.08281

```python

```


```python
import numpy as np
from matplotlib import pyplot as plt
import sys
import sympy as sm
sys.displayhook = sm.pprint

data_points1 = np.array([
    [98.2, 48.25],
    [93.2, 46.97],
    [88.0, 46.17],
    [83.2, 45.20],
    [78.0, 45.13],
    [73.0, 45.57],
    [68.2, 46.90],
    [63.1, 52.19],
    [58.0, 60.11]
])

data_points2 = np.array([
    [98.1, 48.32],
    [93.1, 47.39],
    [88.2, 46.17],
    [83.1, 45.64],
    [78.2, 45.16],
    [73.1, 45.48],
    [68.1, 47.06],
    [63.2, 50.86],
    [58.2, 60.00]
])

data_points2[...,0] = 100.4 - data_points2[...,0]
data_points2 = data_points2[::-1]

data_points1[...,1] = data_points1[...,1]/30.00
data_points2[...,1] = data_points2[...,1]/30.00

data_points = np.vstack((data_points1, data_points2))
plt.scatter(data_points[...,0], data_points[...,1])
data_points
```

```python
coeff1 = np.polyfit(data_points1[...,0], data_points1[...,1], 4)
coeff2 = np.polyfit(data_points2[...,0], data_points2[...,1], 4)

print(coeff1)
print(coeff2)
```

    [ 5.58229134e-07 -1.99843500e-04  2.69306057e-02 -1.61301661e+00
      3.76415759e+01]
    [ 9.53073645e-07 -5.55317330e-05  1.24517197e-03 -1.69846774e-02
      1.64688604e+00]

```python
def PolyCoefficients(x, coeffs):
    """ Returns a polynomial for ``x`` for the ``coeffs`` provided.

    The coefficients must be in desending order (``x**o``...``x**0``)
    """
    o = len(coeffs)
    coeffs = coeffs[::-1]
    y = 0
    for i in range(o):
        y += coeffs[i]*x**i
    return y
```

```python
PolyCoefficients(data_points1[...,0], coeff1)
x1 = np.linspace(data_points1[0,0], data_points1[-1:,0])
x2 = np.linspace(data_points2[0,0], data_points2[-1:,0])
```

```python
x= sm.Symbol('x')
y= sm.Symbol('y')
f1 = PolyCoefficients(x, coeff1)
f2 = PolyCoefficients(x, coeff2)
print("f1 = ", f1)
print()
isect1 = sm.solve(f1 - 1.53, x)[:2]
print(isect1, '\n\n')

print("f2 = ", f2)
print()
isect2 = sm.solve(f2 - 1.53, x)[:2]
print(isect2, '\n\n')
```

    f1 =  5.58229133990043e-7*x**4 - 0.00019984349955394*x**3 + 0.0269306056551367*x**2 - 1.61301660706959*x + 37.6415758764268

    [71.3421824344919, 86.9715230449037]

    f2 =  9.53073645285598e-7*x**4 - 5.55317329531201e-5*x**3 + 0.00124517197024031*x**2 - 0.0169846773572684*x + 1.64688604305746

    [15.7365571264276, 29.9404690291774]

```python
def lineeqn(xo):
    return line[0]*xo + line[1]

plt.plot(data_points[...,0], data_points[...,1], 'b+')
plt.plot(x1, PolyCoefficients(x1, coeff1), 'r')
plt.plot(x2, PolyCoefficients(x2, coeff2), 'r')
plt.plot([0, 100], [1.53]*2)
plt.plot(isect1+isect2, [1.53]*4, 'og')
plt.grid()
plt.show()
```