import numpy as np
from numpy.core.function_base import linspace
import scipy
import scipy.stats
import matplotlib.pyplot as plt
shape = 2
scale = 3
rv_gamma = scipy.stats.gamma(shape, scale=scale)
rv_inv_gamma = scipy.stats.invgamma(shape, scale=scale)
l = 1000
sample_gamma = rv_gamma.rvs(size=[l])
sample_inv_gamma = rv_inv_gamma.rvs(size=[l])
axes = []
fig,ax = plt.subplots()
ax.hist(sample_gamma, bins="auto", density=True, color=[1,0.4,0.4,0.4], label="gamma distribution")
axes.append(ax)
for i in range(2):
    axes.append(ax.twinx())
axes[1].hist(sample_inv_gamma, bins="auto", density=True, color=[0.4,0.4,1,0.4], label="inv-gamma distribution")

x = np.linspace(1e-8,20)
def pdf_gamma(x,shape, scale):
    return 1/(scipy.special.gamma(shape)*shape**scale) * x**(shape-1) * np.exp(-x/scale)
def pdf_inv_gamma(x, shape, scale):
    return scale**shape /scipy.special.gamma(shape) * x**(-shape-1) * np.exp(-scale/x)
y_gamma = scipy.stats.gamma.pdf(x, shape, scale=scale)
y_gamma_m = pdf_gamma(x, shape, scale)
y_inv_gamma = pdf_inv_gamma(x,shape, scale)
axes[2].plot(x,y_gamma, color=[1,0.4,0.4,0.9])
axes[2].plot(x,y_gamma_m, color=[0.4,1,0.4,0.9])
axes[2].plot(x,y_inv_gamma, color=[0.4,0.4,1,0.9])
for ax in axes:
    ax.set_xlim(0,20)
    ax.set_ylim(0,1.5)
plt.savefig("./q3_explore_2.png")


import streamlit as st

import math
import numpy as np
from  matplotlib import pyplot as plt
from  plotly import graph_objects as go
from typing import List
import pandas as pd

interval = st.sidebar.slider(
    "Interval",
    min_value=0.000001,
    max_value=1.0,
    step=0.00001,
    value=0.5
)
Δt = st.sidebar.number_input(
    "Choose Δt for computing derivative:",
    min_value=0.00001,
    max_value=0.1,
    value=0.001,
    step=0.05
)

def _plot_exponentials():
    base = 1

    t = np.arange(0., 5., interval)
    fig, ax = plt.subplots()

    fig = go.Figure()

    for b in range(base, base + 4):
        ax.plot(t, b ** t, label=f"a = {b}")
        fig.add_trace(go.Scatter(x=t, y=b**t, showlegend=True, name=f"a = {b}"))

    fig.update_layout(title="Plotting a^t vs t", xaxis_title="t", yaxis_title="a ^ t")
    st.plotly_chart(fig, use_container_width=True)

st.markdown(
    f"""
    # Exponentials, Derivatives and `e`

    In this article, we will observe some interesting properties of exponential functions and how the magical
    constant `e` governs all exponential functions and their derivatives.

    ## Exponential Functions

    To refresh your memory, exponential functions are typically expressed as follows.
    let `f(t)` be an exponential function of `t` that operates on some constant number `a`:
    """
)

st.latex(
    r"""
    f(t) = a ^ t
    """
)

st.markdown(
    """
    Let's start out by plotting some familiar exponential values.
    """
)

_plot_exponentials()

st.markdown("""
    ## Derivatives of Exponential Functions

    Recall that the expression for computing the derivative of any function `f(t)` is:
""")

st.latex(
    r"""
    f'(t) = \frac{Δf}{Δt} = \frac{f(t + Δt) - f(t)}{Δt} \hspace{3 pt} \left( \text {where \Δt is small enough.} \right)
    """
)

st.markdown("""
    Without further ado, let's just start plotting `f'(t)` and compare it against `f(t)`.
""")

def _plot_exponential_derivative() -> None:

    t = np.arange(0., 10., interval)

    a = st.slider(
        "Choose a value of `a` as the base of the exponential function (example: 2.0):",
        min_value=0.01, max_value=10.0, value=2.0, step=0.01
    )

    fig, ax = plt.subplots()
    ax.plot(t, a ** t, 'r+', label="f(t)")
    ax.plot(
        t, (a ** (t + Δt) - a ** t) / Δt, 'bo', label="f'(t)",
    )
    ax.plot(t, ((a ** (t + Δt) - a ** t) / Δt) / (a ** t), label="f'(t) / f(t)" )

    _ = ax.legend()
    ax.set_xlabel("t")
    fig.suptitle(f"Base: {a}, c: {((a ** Δt) - 1) / Δt} where c = f'(t) / f(t)")
    st.pyplot(fig, use_container_width=True)
    # st.plotly_chart(fig, use_container_width=True)

_plot_exponential_derivative()

st.markdown("""
    The most important observation from the above graph is the ratio `f'(t) / f(t)` is a constant
    value for all values of `t`.  We will see shortly what that is the case, but the key insight
    from this observation is that **the rate of change of an exponential function at some `t` is
    proportional to the value of the exponential function at `t`.**  Mathematically speaking:
    """)

st.latex(r"""f(t) = a^t, f'(t) = K a^t""")

st.markdown("""
    By playing with the slider in the chart above, you can see that the value of `K` is different for
    different values of `a`.

    **It is worth conteplating at this point if there exists a value of `a` for which `K` is equal to `1`.**

    Without getting entangled in the mathematical expressions, let's just try to figure out the value of
    `a` for which `K = 1` by tweaking the slider in the above chart.  Give it a try!

    >Hint: If you play with the `a` number for a bit, you will see that the scaling
    >factor `K` is close to `1.0` when `a` is approximately `2.72`.  Keep this number in mind,
    >as it will appear once again later in this tutorial.
    """
)

st.markdown("""
    ## Magical Multiplier to Rule All Exponential Derivatives
    Now, let's approach the same setup from a different perspective.

    Recall from the earlier sections that `f'(t) = f(t) * c` where:
""")

st.latex(r'c = \left(\frac{a^{dt} - 1}{dt}\right) \text{for some small value of dt.}')

st.markdown("""
    To gain more insight into the behavior of this multiplier, we will observe its values
    for various values of `a`. For convention, let's rewrite it as a function in `a`:
""")
st.latex(r'g(a) = \left(\frac{a^{dt} - 1}{dt}\right) \text{for some small value of dt.}')

def _plot_multiplier_vs_base() -> None:
    interval = st.slider(
        "Interval:", min_value=0.000001, max_value=1.0, step=0.00001, value=0.5, key='i4'
    )
    bases = np.arange(1., 50., interval)

    dt = st.number_input(
        "Choose `dt` for computing derivative:",
        min_value=0.00001, max_value=0.1, value=0.001, step=0.05, key='dt1'
    )

    fig, ax = plt.subplots()
    ax.plot(bases, (bases ** dt - 1) / dt, '+')
    ax.set_xlabel('a')
    ax.set_ylabel('g(a)')
    st.plotly_chart(fig, use_container_width=True)

    g = [
        [2, (2 ** dt - 1) / dt],
        [4, (4 ** dt - 1) / dt],
        [5, (5 ** dt - 1) / dt],
        [8, (8 ** dt - 1) / dt],
        [25, (25 ** dt - 1) / dt],
        [64, (64 ** dt - 1) / dt],
    ]
    st.dataframe(pd.DataFrame.from_records(g, columns=['a', 'g(a)']))

_plot_multiplier_vs_base()

st.markdown("""
    Notice the peculiar shape of this chart.
    To gain more insight in the shape, observe the hand-picked values in the table.

    From the above table, you can see that:
""")

st.latex(r'''
    g(a^2) = 2 g(a)
''')

st.markdown("""
    This is a strong hint that `g(a)` is a logarithmic function!
    But it is not quite the [common logarithm](https://en.wikipedia.org/wiki/Common_logarithm),
    because we know that `log` of `2` to the base `10` is approximately `0.30103`.

    **So what is really the base of this logarithmic function?**

    ### Finding the Base of of Logarithmic Multiplier
    Let `x` be the unknown base of the logarithm function represented by `g`:
""")

st.latex(r'''
    g(a) = log_x (a) \hspace{2 pt} \text {where x represents the unknown base of this log function.}
''')

st.write("Let's take the specific examples from the table above:")
st.latex(r"""log_x(2) = 0.6931 \dots""")
st.latex(r"""log_x(4) = 1.3873 \dots""")
st.latex(r"""log_x(5) = 1.6107 \dots""")
st.latex(r"""log_x(8) = 2.0816 \dots""")
st.latex(r"""log_x(16) = 2.7764 \dots""")
st.latex(r"""log_x(25) = 3.2241 \dots""")
st.latex(r"""log_x(64) = 4.1675 \dots""")

st.markdown("""
    To empirically find `x` in the above expression, let's just plot the expression for
    various values of `x`.
""")

def _plot_exponents_for_x():
    interval = st.slider(
        "Interval:", min_value=0.000001, max_value=1.0, step=0.00001, value=0.5, key='i5'
    )
    x = np.arange(1., 10., interval)

    a = st.number_input(
        "Choose a value of `a` for the exponential (example: 2.0):",
        min_value=0.01, max_value=10.0, value=2.0, step=1.0
    )

    fig, ax = plt.subplots()
    ax.plot(x, np.log(a) / np.log(x), '+')
    ax.set_xlabel('x')
    ax.set_ylabel('log(a) to the base x')
    fig.suptitle(f"a: {a}")
    st.plotly_chart(fig, use_container_width=True)

_plot_exponents_for_x()

st.markdown(f"""
    In this chart, try varying the value of `a`, and for each value of `a`, try to find
    the value of `x` that corresponds to a y-axis value that matches `log_x(a)` in the table above.

    For example, if we set `a` to `2.0`, what is the value of `x` that is close to the value
    `0.6931...` on the y-axis?  The answer is a number between `2.7` and `2.8`, which is
    suspiciously close to the number we observed earlier.

    As another example, if we set `a` to `5.0`, what is the value of `x` that is close to the value
    `0.6931...` on the y-axis?  The answer is again a number between `2.7` and `2.8`, which is
    suspiciously close to the number we observed earlier.

    This in fact is the magical number `e` that is the base of the logarithmic function represented by `g`!
""")

st.latex(r"""g(a) = log_e(a) = ln(a)""")

st.markdown("""
    >The more precise value of `e` according to the `numpy` package is {np.e}.

    ## Connecting the Dots
    This magical number `e` underpins the phenomenon where the rate of change of an exponential function `a^t` at
    a certain value of `t` is proportional to the value `a^t` multiplied by the log of `a` to the base `e`:
""")

st.latex(r"""f(t) = a^t""")
st.latex(r"""
    f'(t) = a^t log_e(a) = a^t ln(a)
""")

st.markdown("""
    The natural extension of this expression is when `a = e`, `f'(t) = f(t)`:
""")

st.latex(r"""f(t) = e^t""")
st.latex(r"""
    f'(t) = e^t log_e(e) = e^t
""")

st.markdown("""
    ## References

    This tutorial is basically a watered down version of this mindblowing [YouTube video](https://www.youtube.com/watch?v=m2MIpDrF7Es).

    I created this tutorial just to convince myself of all the math in the video and also to play with Streamlit open-source library.
    Do check out the documentation at [https://docs.streamlit.io/](https://docs.streamlit.io/) and follow along interesting discussions
    at [https://discuss.streamlit.io/](https://discuss.streamlit.io/).

""")


import numpy as np

def float_is_zero(v, eps=1e-6):
    """ Test if floating point number is zero. """
    return abs(v) < eps

def special_div(num, den):
    """ Return num/dev with the special rule
    that 0/0 is 0. """
    if float_is_zero(num) and float_is_zero(den):
        return 0.0
    else:
        return num / den

def B(j, p, x, knots):
    """ Compute B-splines using recursive definition. """
    if p == 0:
        if knots[j] <= x < knots[j+1]:
            return 1.0
        else:
            return 0.0
    else:
        left = special_div((x-knots[j])*B(j,p-1,x,knots), knots[j+p]-knots[j])
        right = special_div((knots[j+1+p]-x)*B(j+1,p-1,x,knots), knots[j+1+p]-knots[j+1])
        return left + right

def get_mu(x, knots, mu=None):
    """
    Find the knot interval index (mu) for a given value x
    This is a prerequisite for alg 2.20 and 2.21.
    Optional to supply a suggestion to try first.
    Throws on error.
    """
    if mu != None:
        if x >= knots[mu] and x < knots[mu+1]:
            return mu
    for mu in range(0, len(knots)-1):
        if x >= knots[mu] and x < knots[mu+1]:
            return mu
    raise RuntimeError("Illegal knot vector or x value")

def R_mat(k, mu, knots, x):
    """
    Compute k'th B-spline matrix (k=1...)
    mu: so that t_{mu} <= x < t_{mu+1}
    knots: the knot vector
    x: value to evaluate in
    """
    res = np.zeros((k,k+1))
    for row in range(k):
        common_denom = knots[mu+(row+1)] - knots[mu+(row+1)-k]
        res[row,row] = special_div(knots[mu+(row+1)] - x, common_denom)
        res[row,row+1] = special_div(x - knots[mu + (row+1) - k], common_denom)
    return res

def alg_220(p, knots, control_points, x, mu=None):
    """
    Compute the spline function value at x
    p: degree
    knots: knots vector
    control_points:
    x: value to evaluate at
    """
    if mu == None:
        # auto-determine mu
        mu = get_mu(x, knots)
    c = np.array(control_points[(mu-p):(mu+1)])
    assert( len(c) == p+1 )
    for k in range(p, 0, -1):   # k = p...1
        c = R_mat(k, mu, knots, x).dot(c)
    return c

def alg_221(p, knots, x):
    """
    Evaluate all p+1 nonzero B-splines at x
    p: degree
    knots: knot vector
    x: value to evaluate at
    """
    mu = get_mu(x, knots)
    B = np.array([1.0])
    for k in range(1, p+1):
        B = B.dot(R_mat(k, mu, knots, x))
    return B

def render_spline(p, knots, control_points, ts):
    """
    Compute points on a spline function using the straightforward
    implementation of the recurrence relation for the B-splines.
    """

    ys = []
    for t in ts:
        y = 0.0
        for j in range(0, len(control_points)):
            y += B(j, p, t, knots)*control_points[j]
        ys.append(y)
    return ys

def render_spline_alg220(p, knots, control_points, ts):
    # TODO: verify input data
    # TODO: reuse get_mu() and alg_220()
    xs = []
    mu = None
    for t in ts:
        # Try the last mu value first, and then search if needed
        if mu == None or not (t >= knots[mu] and t < knots[mu+1]):
            mu = None
            for i in range(0, len(knots)-1):
                if t >= knots[i] and t < knots[i+1]:
                    mu = i
            if mu == None: raise Exception("Unable to determine mu")

        # Compute the B-spline vector
        B_vec = np.array([[1]])
        for k in range(1, p+1):
            Rk = R_mat(k, mu, knots, t)
            B_vec = B_vec.dot(Rk)

        # Dot with correct part of control point vector
        c0 = control_points[(mu-p):(mu+1)]
        x = B_vec.dot(c0)[0]
        xs.append(x)
    return xs

def uniform_regular_knot_vector(n, p, t0=0.0, t1=1.0):
    """
    Create a p+1-regular uniform knot vector for
    a given number of control points
    Throws if n is too small
    """

    # The minimum length of a p+1-regular knot vector
    # is 2*(p+1)
    if n < p+1:
        raise RuntimeError("Too small n for a uniform regular knot vector")

    # p+1 copies of t0 left and p+1 copies of t1 right
    # but one of each in linspace
    return [t0]*p + list(np.linspace(t0, t1, n+1-p)) + [t1]*p

def control_points(p, knots):
    """
    Return the control point abscissa for the control polygon
    of a one-dimensional spline.
    """
    knots = np.array(knots)
    abscissas = []
    for i in range(len(knots)-p-1):
        part = knots[(i+1):(i+1+p)]
        abscissas.append(np.mean(part))
    return abscissas

def render_tensor_prod_spline(p1, p2, knots1, knots2, control_points, us, vs):
    """ Slow ref. impl. """
    res = np.empty((len(us), len(vs)))
    n1, n2 = control_points.shape
    assert(n1 == len(knots1) - p1 - 1)
    assert(n2 == len(knots2) - p2 - 1)
    for u_i,u in enumerate(us):
        for v_i,v in enumerate(vs):
            s = 0.0
            for i in range(n1):
                for j in range(n2):
                    s += control_points[i,j]*B(i, p1, u, knots1)*B(j, p2, v, knots2)
            res[u_i, v_i] = s
    return res

def least_squares_surface_fit(xs, ys, zs, ws1, ws2, p1, p2, knots1, knots2):
    """
    Data points (xs[i], ys[j], zs[i,j]) (i=1..m1, j=1..m2)
    ws1[i] weights (i=1..m1)
    ws2[j] weights (j=1..m2)
    Spline spaces S1 and S2 implicitly defined by
    (p1, knots1) and (p2, knots2):
        n1=len(knots1)-(p1+1)
        n2=len(knots2)-(p2+1)
    Returns n1 x n2 matrix of coefficients
    """
    m1, m2 = zs.shape
    assert(m1 == len(xs))
    assert(m2 == len(ys))
    assert(len(ws1) == m1)
    assert(len(ws2) == m2)

    n1 = len(knots1) - (p1+1)
    n2 = len(knots2) - (p2+1)

    # Create matrix A
    A = np.empty((m1, n1))
    for i in range(m1):
        for q in range(n1):
            A[i, q] = np.sqrt(ws1[i])*B(q, p1, xs[i], knots1)

    # Create matrix _B
    _B = np.empty((m2, n2))
    for j in range(m2):
        for r in range(n2):
            _B[j, r] = np.sqrt(ws2[j])*B(r, p2, ys[j], knots2)

    # Create matrix G
    G = np.empty((m1, m2))
    for i in range(m1):
        for j in range(m2):
            G[i, j] = np.sqrt(ws1[i])*np.sqrt(ws2[j])*zs[i, j]

    # Compute the coefficient matrix from eq. (7.20)
    A_trans = A.transpose()
    M1 = np.linalg.inv(A_trans.dot(A))
    M2 = np.linalg.inv(_B.transpose().dot(_B))
    C = M1.dot(A_trans).dot(G).dot(_B).dot(M2)

    return C

def lsq_spline_fit(xs, ys, knots, p, ws=None):
    """
    Returns spline coefficients for least-squares
    fit of function samples (x_i, y_i) on an arbitrary
    knot vector and degree.
    """
    if ws == None:
        ws = np.ones((len(xs),))
    m = len(xs)
    n = len(knots) - p - 1  # number of control points in approximation
    assert(m == len(ys))

    # Create matrix A
    A = np.empty((m,n))
    for row in range(m):
        for col in range(n):
            A[row, col] = np.sqrt(ws[row])*B(col, p, xs[row], knots)

    # Create vector b
    b = np.empty((len(xs),))
    for i in range(m):
        b[i] = np.sqrt(ws[i])*ys[i]

    # Compute least-squares coefficients
    Atrans = A.T
    c = np.linalg.inv(Atrans.dot(A)).dot(Atrans).dot(b)

    return c

def render_tensor_product_surface_alg_221(p1, p2, knots1, knots2, control_points, xs, ys):
    """ Evaluate at xs,ys """
    xs = np.array(xs)
    ys = np.array(ys)
    res = np.empty((xs.shape[0], ys.shape[0]))

    for x_i,x in enumerate(xs):
        for y_i, y in enumerate(ys):
            # Find the knot indices
            mv = get_mu(x, knots1)
            mu = get_mu(y, knots2)

            # Extract the correct part of the control point matrix
            C = control_points[(mv-p1):(mv+1), (mu-p2):(mu+1)]

            # Compute the non-zero basis functions
            Bx = alg_221(p1, knots1, x)
            By = alg_221(p2, knots2, y)

            value = Bx.dot(C).dot(By)
            res[x_i, y_i] = value

    return res

def B_derivative(j, p, x, knots):
    """
    Evaluate the derivative of Bj,p(x)
    p must be greater than or equal to 1
    Using theorem 3.16 from the book.
    """
    if p < 1: raise RuntimeError("p must be greater than or equal to 1")
    left = special_div(B(j, p-1, x, knots), (knots[j+p]-knots[j]) )
    right = special_div(B(j+1,p-1, x, knots), (knots[j+p+1] - knots[j+1]) )
    return (left - right)*p

def general_spline_interpolation(xs, ys, p, knots=None):
    """
    NOTE: SLOW SINCE IT USES B()
    xs,ys:  interpolation points
    p:      degree
    knots:  If None, use p+1-regular from xs[0] to slightly past x[1]

    returns cs, knots
    """

    # number of interpolation points (and also control points)
    m = len(xs)
    assert(len(ys) == m)

    # use p+1-regular knot vector with ends equal to first sample and slightly
    # past last sample
    if knots == None:
        knots = uniform_regular_knot_vector(m, p, t0=xs[0], t1=xs[-1]+0.001)

    # create matrix A
    A = np.zeros((m,m))
    for row in range(m):
        for col in range(m):
            A[row, col] = B(col, p, xs[row], knots)

    # compute control points
    cs = np.linalg.inv(A).dot(np.array(ys))
    return cs, knots

def alpha(j, p, i, coarse_knots, refined_knots):
    """ Compute B-splines using recursive definition. """
    x = refined_knots[i+p]
    knots = coarse_knots
    if p == 0:
        if knots[j] <= x < knots[j+1]:
            return 1.0
        else:
            return 0.0
    else:
        left = special_div((x-knots[j])*alpha(j,p-1,i,coarse_knots, refined_knots), knots[j+p]-knots[j])
        right = special_div((knots[j+1+p]-x)*alpha(j+1,p-1,i,coarse_knots,refined_knots), knots[j+1+p]-knots[j+1])
        return left + right

def oslo_alg1(p, coarse_knots, refined_knots):
    """
    Compute knot insertion matrix using Oslo-algorithm 1.
    Returns the m x n knot insertion matrix.
    """
    n = len(coarse_knots) - p - 1
    m = len(refined_knots) - p - 1
    A = np.zeros((m, n,))
    for i in range(m):
        mu = get_mu(refined_knots[i], coarse_knots)
        if p == 0:
            res = 1
        else:
            res = R_mat(1, mu, coarse_knots, refined_knots[i+1])
            for k in range(2, p+1):
                res = res.dot(R_mat(k, mu, coarse_knots, refined_knots[i+k]))
        A[i, mu-p:(mu+1)] = res
    return A

def oslo_alg2(p, cs, coarse_knots, refined_knots):
    """
    Compute control points relative to refined knot vector
    using Oslo-algorithm 2
    Returns length-m control point vector.
    """
    n = len(coarse_knots) - p - 1
    m = len(refined_knots) - p - 1
    b = np.zeros((m,))
    for i in range(m):
        mu = get_mu(refined_knots[i], coarse_knots)
        if p == 0:
            res = cs[mu]
        else:
            _cs = cs[(mu-p):(mu+1)]
            res = R_mat(1, mu, coarse_knots, refined_knots[i+1])
            for k in range(2, p+1):
                res = res.dot(R_mat(k, mu, coarse_knots, refined_knots[i+k]))
            res = res.dot(_cs)
        b[i] = res
    return b

def test_render_tensor_product_surface_alg_221_1():
    """
    Compare using algorithm 2.21 with using the recurrence
    relation.
    """

    n1 = 10 # x
    n2 = 10 # y
    p1 = 3
    p2 = 3
    cs = np.random.uniform(low=0.0, high = 10.0, size=(n1, n2))
    knots1 = uniform_regular_knot_vector(n1, p1)
    knots2 = uniform_regular_knot_vector(n2, p2)

    # Where to evaluate
    xs = np.linspace(knots1[0], knots1[-1]-0.01, 10)
    ys = np.linspace(knots2[0], knots2[-1]-0.01, 10)
    for x in xs:
        for y in ys:
            f1 = render_tensor_product_surface_alg_221(p1, p2, knots1, knots2, cs, [x], [y])
            f2 = render_tensor_prod_spline(p1, p2, knots1, knots2, cs, [x], [y])
            assert(float_is_zero(f1-f2))

def test_alg_220_1():
    """
    Verify that alg 2.20 agrees with the direct implementation
    of algorithm 2.20
    """

    p = 3
    n = 10
    knots = uniform_regular_knot_vector(n, p)
    cs = range(10)
    for t in np.linspace(0.0, 0.99, 100):
        # Evaluate with recurrence relation
        x1 = render_spline(p, knots, cs, [t])
        # Evaluate with alg 2.20
        x2 = alg_220(p, knots, cs, t)
        assert(float_is_zero(x1-x2))

def test_get_mu_1():
    """
    Verify that the knot interval search works
    on some simple cases
    """
    assert(get_mu(0.1, [0.0, 1.0]) == 0)
    assert(get_mu(1.0, [0.0, 1.0, 2.0]) == 1)
    assert(get_mu(1.0, [0.0, 0.0, 0.0, 0.9, 1.1, 1.1, 1.1]) == 3)

def test_alg_221_1():
    """
    Verify that algorithm 2.21 produces the same
    results as the direct implementation of the
    recurrence relation.
    """
    n = 5
    p = 3
    knots = uniform_regular_knot_vector(n, p)

    ts = np.linspace(knots[0], knots[-1]-0.01, 10)

    for t in ts:
        # Compute all B-splines at once with alg 2.21
        B1 = alg_221(p, knots, t)
        mu = get_mu(t, knots)

        # Compute one by one with recurrence relation
        for j in range(p+1):
            B2 = B(j+mu-p, p, t, knots)
            assert( float_is_zero(B2 - B1[j]) )

def test_bspline_derivative():
    """
    Compare B-spline derivatives with numerical differentiation
    of the B-splines.
    """
    import matplotlib.pyplot as plt
    p = 2
    knots = [0.0, 0.0, 0.0, 1.0, 1.4, 1.9, 2.1, 2.1, 2.1]
    ts = np.linspace(knots[0], knots[-1]-0.001, 10000)
    for j in range(0, len(knots)-p-1):
        plt.figure(j)
        xs = map(lambda x: B(j, p, x, knots), ts)
        xs_der = map(lambda x: B_derivative(j, p, x, knots), ts)
        plt.subplot(2,1,1)
        plt.plot(ts, xs_der, label='Analytical')

        xs_num_der = np.diff(xs)/np.diff(ts)
        plt.plot(ts[:-1], xs_num_der, label='Numerical')
        plt.title('Basis function derivative %d' % j)
        plt.subplot(2,1,2)
        error = xs_der[:-1]-xs_num_der
        plt.plot(error)
        plt.title('Error')

    plt.show()

if __name__ == '__main__':
    test_get_mu_1()
    test_alg_220_1()
    test_alg_221_1()
    test_render_tensor_product_surface_alg_221_1()
    test_bspline_derivative()


import time

import numpy as np

def jacobi(A, b, max_iter_time=5000, min_iter_time=100, tolerance=1e-10):
    """
    Jacobi iteration for solving the linear equations with below form:
        A * u = b

    ---------------
    :param A: (N, N) array_like
        Matrix A (n*n size). Square input data.
    :param b: (N, 1) array_like
        Vector b (n*1 size). Input data for the right hand side.
    :param max_iter_time: int, optional
        Maximal iteration times.
    :param min_iter_time: int, optional
        Minimal iteration times
    :param tolerance: float, optional
        The difference tolerance for adjacent u solutions.
    ---------------
    :return:
        The solution array u. Or suggestive error message.
    """
    start = time.clock()
    end = time.clock()

    A = np.array(A)
    b = np.array(b)
    n, m = A.shape
    if n != m:
        return "The row size of matrix 'A' is not equal to its column size."
    if n != b.shape[0]:
        return "The row size of matrix 'A' is not equal to the size of vector 'b'."
    b.reshape(n)
    aii = np.array([A[i, i] for i in range(n)])
    x_before = np.zeros(n)
    x_cur = np.zeros(n)

    # iteration
    for i in range(max_iter_time):
        for r in range(n):
            remain = np.dot(A[r, :], x_before[:]) - A[r, r] * x_before[r]
            x_cur[r] = (b[r] - remain) / aii[r]
        if max(abs(x_cur - x_before)) < tolerance and i > min_iter_time:
            print('Jacobi Iteration:', i + 1)
            print('Time past:', time.clock() - start)
            return x_cur
        else:
            x_cur, x_before = x_before, x_cur
    print("Maximal iteration times reached.")

    print('Jacobi Iteration:', i + 1)
    print('Time past:', time.clock() - start)
    return x_cur

def gauss_seidel(A, b, max_iter_time=5000, min_iter_time=100, tolerance=1e-10):
    """
    Gauss-Seidel iteration for solving the linear equations with below form:
        A * u = b

    ---------------
    :param A: (N, N) array_like
        Matrix A (n*n size). Square input data.
    :param b: (N, 1) array_like
        Vector b (n*1 size). Input data for the right hand side.
    :param max_iter_time: int, optional
        Maximal iteration times.
    :param min_iter_time: int, optional
        Minimal iteration times
    :param tolerance: float, optional
        The difference tolerance for adjacent u solutions.
    ---------------
    :return:
        The solution array u. Or suggestive error message.
    """
    start = time.clock()

    A = np.array(A)
    b = np.array(b)
    n, m = A.shape
    if n != m:
        return "The row size of matrix 'A' is not equal to its column size."
    if n != b.shape[0]:
        return "The row size of matrix 'A' is not equal to the size of vector 'b'."
    b.reshape(n)
    aii = np.array([A[i, i] for i in range(n)])
    x_before = np.zeros(n)
    x_cur = np.zeros(n)

    # iteration
    for i in range(max_iter_time):
        for r in range(n):
            remain = np.dot(A[r, :], x_cur[:]) - A[r, r] * x_cur[r]
            x_cur[r] = (b[r] - remain) / aii[r]
        if max(abs(x_cur - x_before)) < tolerance and i > min_iter_time:
            print('Gauss-Seidel Iterations:', i + 1)
            print('Time past:', time.clock() - start)
            return x_cur

        x_before = np.copy(x_cur)
    print("Maximal iteration times reached.")
    print('Gauss-Seidel Iterations:', i + 1)
    print('Time past:', time.clock() - start)
    return x_cur

# just for simple test
if __name__ == '__main__':
    from scipy.linalg import solve

    A = np.array([[8, -3, 2], [4, 11, -1], [6, 3, 12]])
    b = np.array([[20], [33], [36]])

    s1 = solve(A, b).reshape(len(b))
    s2 = jacobi(A, b)
    s3 = gauss_seidel(A, b)

    print(s1, s2, s3)
    print(np.array(s1) - np.array(s2))


import operator
import numpy as np
import time

a = np.random.randn(1000000)
b = np.random.randn(1000000)

# Simple addition
t1 = time.time()
c0 = a + b
print("Duration of a + b:\n{}".format(time.time() - t1))

# Operator.add()
t1 = time.time()
c1 = operator.add(a, b)
print("Duration of operator.add():\n{}".format(time.time() - t1))

# Using np.vectorize()
t1 = time.time()
c2 = np.vectorize(operator.add)(a, b)
print("Duration of np.vectorize():\n{}".format(time.time() - t1))

# Using np.add(a, b)
t1 = time.time()
c4 = np.add(a, b)
print("Duration of `np.add(a, b)`:\n{}".format(time.time() - t1))

print("(c0 == c4).all():\n{}".format((c0 == c4).all()))

#"So, the key to using NumPy is simple: it's not about moving the inner loop into NumPy, it's about getting rid of all Python functions in that inner loop. Only call natively-vectorized functions like np.add, or use operators like +, on the arrays."

#"This is where NumPy programming becomes an art rather than just literal translation. You have to learn all the functions NumPy includes, and get to know which ones to reach for when facing each problem, and the only way to get there is through experience."

# The original works with lists of lists
# for i, ra in enumerate(a):
#         c.append([])
#         for j, ca in enumerate(ra):
#             c[-1].append(ca + b[j][i] * i)
#
# c = a + b.T * np.arange(a.shape[0])


'''
What is Standard Deviation?

Standard deviation is a number that describes how spread out the values are.

A low standard deviation means that most of the numbers are close to the mean (average) value.

A high standard deviation means that the values are spread out over a wider range.

Example: This time we have registered the speed of 7 cars:

speed = [86,87,88,86,87,85,86]

The standard deviation is:

0.9

Meaning that most of the values are within the range of 0.9 from the mean value, which is 86.4.

Let us do the same with a selection of numbers with a wider range:

speed = [32,111,138,28,59,77,97]

The standard deviation is:

37.85

Meaning that most of the values are within the range of 37.85 from the mean value, which is 77.4.

As you can see, a higher standard deviation indicates that the values are spread out over a wider range.

The NumPy module has a method to calculate the standard deviation:
Example

Use the NumPy std() method to find the standard deviation:
'''
import numpy as r
speed=[86,87,88,86,87,85,86]

o=r.std(speed)
print(o)

'''
Variance

Variance is another number that indicates how spread out the values are.

In fact, if you take the square root of the variance, you get the standard deviation!

Or the other way around, if you multiply the standard deviation by itself, you get the variance!

To calculate the variance you have to do as follows:

1. Find the mean:

(32+111+138+28+59+77+97) / 7 = 77.4

2. For each value: find the difference from the mean:

 32 - 77.4 = -45.4
111 - 77.4 =  33.6
138 - 77.4 =  60.6
 28 - 77.4 = -49.4
 59 - 77.4 = -18.4
 77 - 77.4 = - 0.4
 97 - 77.4 =  19.6

3. For each difference: find the square value:

(-45.4)2 = 2061.16
 (33.6)2 = 1128.96
 (60.6)2 = 3672.36
(-49.4)2 = 2440.36
(-18.4)2 =  338.56
(- 0.4)2 =    0.16
 (19.6)2 =  384.16

4. The variance is the average number of these squared differences:

(2061.16+1128.96+3672.36+2440.36+338.56+0.16+384.16) / 7 = 1432.2

Luckily, NumPy has a method to calculate the variance:
Example

Use the NumPy var() method to find the variance:
'''

import numpy as r
speed=[32,111,138,28,59,77,97]

p=r.var(speed)
print(p)

'''Standard Deviation

As we have learned, the formula to find the standard deviation is the square root of the variance:

√1432.25 = 37.85

Or, as in the example from before, use the NumPy to calculate the standard deviation:
Example

Use the NumPy std() method to find the standard deviation:
import numpy
'''
speed = [32,111,138,28,59,77,97]

x = r.std(speed)

print(x)


import numpy as np
from math import *
import matplotlib.pyplot as plt

#This file contains two functions which simulate trajectories of the fractional Brownian motion.

def davies_harte(T, N, H):
    '''
    Generates a sample path of fractional Brownian Motion using the Davies Harte method

    args:
        T:      length of time (in years)
        N:      number of time steps within timeframe
        H:      Hurst parameter
    '''
    gamma = lambda k,H: 0.5*(np.abs(k-1)**(2*H) - 2*np.abs(k)**(2*H) + np.abs(k+1)**(2*H))
    g = [gamma(k,H) for k in range(0,N)];    r = g + [0] + g[::-1][0:N-1]

    # Step 1 (eigenvalues)
    j = np.arange(0,2*N);   k = 2*N-1
    lk = np.fft.fft(r*np.exp(2*np.pi*complex(0,1)*k*j*(1/(2*N))))[::-1]

    # Step 2 (get random variables)
    Vj = np.zeros((2*N,2), dtype=np.complex);
    Vj[0,0] = np.random.standard_normal();  Vj[N,0] = np.random.standard_normal()

    for i in range(1,N):
        Vj1 = np.random.standard_normal();    Vj2 = np.random.standard_normal()
        Vj[i][0] = Vj1; Vj[i][1] = Vj2; Vj[2*N-i][0] = Vj1;    Vj[2*N-i][1] = Vj2

    # Step 3 (compute Z)
    wk = np.zeros(2*N, dtype=np.complex)
    wk[0] = np.sqrt((lk[0]/(2*N)))*Vj[0][0];
    wk[1:N] = np.sqrt(lk[1:N]/(4*N))*((Vj[1:N].T[0]) + (complex(0,1)*Vj[1:N].T[1]))
    wk[N] = np.sqrt((lk[0]/(2*N)))*Vj[N][0]
    wk[N+1:2*N] = np.sqrt(lk[N+1:2*N]/(4*N))*(np.flip(Vj[1:N].T[0]) - (complex(0,1)*np.flip(Vj[1:N].T[1])))

    Z = np.fft.fft(wk);     fGn = Z[0:N]
    fBm = np.cumsum(fGn)*(N**(-H))
    fBm = (T**H)*(fBm)
    path = np.array([0] + list(fBm))
    return path

def fBm(T,N,H,trials):
      '''
    Generates multiple sample path of fractional Brownian Motion using the Davies Harte method

    args:
        T:      length of time (in years)
        N:      number of time steps within timeframe
        H:      Hurst parameter
        trials: number of paths
    '''
    B = np.zeros((N+1,trials))
    for i in range(trials):
        B[:,i] = davies_harte(T,N,H)
    return B


import numpy as np
import matplotlib.pyplot as plt
import math

def rotation(grid_in, pattern = 1):
    if pattern == 1:
       return grid_in.T
    elif pattern == 2:
       return np.flipud(np.fliplr(grid_in))

def hilbert_space_filling_curve(num = 4, ver_bose = False, ver_bose_contour=False):

    n_level = np.log2(num)
    if n_level // 1 != n_level:
        raise ValueError("square grid size not a exponential of 2!")
    elif n_level == 1:
        raise ValueError("grid size should be larger or equal than 4!")

    n_level = n_level.astype('int')

    # start from 2 * 2 grid
    grid_1 = np.array([0, 3, 1, 2]).reshape(2, 2)
    subsize = 4

    for i in range(2, n_level + 1):

        grid_2 = rotation(grid_1, pattern = 1) + subsize

        grid_3 = rotation(grid_1, pattern = 1) + subsize * 2

        grid_4 = rotation(grid_1, pattern = 2) + subsize * 3

        grid_1 = np.vstack((np.hstack((grid_1, grid_2)), np.hstack((grid_4, grid_3))))

        grid_1 = rotation(grid_1, pattern = 1)

        subsize *= 4

    if ver_bose:

        x_coords = np.argsort(grid_1.flatten()) // num
        y_coords = np.argsort(grid_1.flatten()) % num

        fig, ax = plt.subplots(figsize=(20,20))
        ax.set_title("Hilbert space-filling curve on a %d * %d square grid" % (num, num), fontsize = 25)

        ax.plot(x_coords, y_coords, color = "black")

        plt.show()

    if ver_bose_contour:

        fig, ax = plt.subplots(figsize=(20,20))
        ax.set_title("Hilbert space-filling curve on a %d * %d square grid" % (num, num), fontsize = 25)
        xx, yy = np.meshgrid(np.arange(0, num), np.arange(0, num))
        plot_levels = [i * num // 2 - 0.5 for i in range(1, num * 2 - 1)]
        plot_levels.insert(0, 0)
        plot_levels.append(num ** 2)

        cset = plt.contourf(xx, yy, grid_1.T, levels=plot_levels, cmap=None)
        fig.colorbar(cset, shrink=0.5, aspect=5)

        plt.show()

    return np.argsort(grid_1.flatten())

def inverse_ordering(order_index):
    return np.argsort(order_index)

# index_hilbert = hilbert_space_filling_curve(8)

# element64 = np.arange(1, 65)

# inverse_map = np.argsort(index_hilbert)
# inverse_map

# (element64[index_hilbert])[inverse_ordering(index_hilbert)]

# Hilbert_ordering

# Hilbert_ordering[inverse_map]


from sympy import *
from sympy.parsing.sympy_parser import parse_expr
import sys

f = None
f_prime = None

def main():
    x = Symbol('x')

    # polynomial to evaluate
    global f
    f = parse_expr('x**2-2') #you can also read from terminal using sys.agrv

    global f_prime
    f_prime = f.diff(x)  # symbolic derivative with respect to x

    f = lambdify(x, f)  # pass evaulation f to f
    f_prime = lambdify(x, f_prime)

    secant(1,2,10**-4,100) #method to use

def bisec(a, b, error, N):
    # Initialization
    a = float(a)
    b = float(b)
    error = float(error)  # error tolerance

    for i in range(0, N):
        print("iteration", i + 1)
        c = (a + b) / 2

        print("a=", a, " b=", b, "c=", c)

        print("f(a)=", f(a), " f(c)=", f(c))

        if(f(c) == 0):
            print("the solution is", c)
            break
        elif(f(a)*f(c)<0):
            print("sign negative")
            b = c
        else:
            print("sign positive")
            a = c

        print("error=",abs(b-a))
        print("the root is in between",a ,b)

        if(abs(b-a)<error):
            print("the solution is ~",c)
            break
        print()

def newton(x0, error, N):
    x0 = float(x0)
    error = float(error)

    for i in range (0, N):
        print("iteration", i+1)
        print("x0=", x0)

        x1 = (x0)-f(x0)/f_prime(x0)
        print("x1=", x1)

        e = abs(x1-x0)
        print("|x0-x1|=", e)
        print("f(x)=", f(x0))
        print("f'(x)=",f_prime(x0))
        x0 = x1

        if(e<=error):
            print("the solution is ~", x0)
            break
        print()

def secant(x0, x1, error, N):
    x0 = float(x0)
    error = float(error)

    for i in range (0, N):
        print("iteration", i+1)
        print("x0=", x0)
        print("x1=", x1)

        x2 = x1-(f(x1)*(x1-x0))/(f(x1)-f(x0))
        print("f(x0)=", f(x0))
        print("f(x1)=", f(x1))
        print("x2=", x2)

        e = abs(x2-x1)
        print("error=", e)
        print()
        x0 = x1
        x1 = x2

        if(e<=error):
            print(" the solution is ~", x1)
            break

main()


"""
黄金比
"""

def golden_ratio(digit: int) -> str:
	from sympy import N, sqrt
	return str(N("(1 + sqrt(5)) / 2", digit))

if __name__ == "__main__":
	digit = 10000
	print(golden_ratio(digit))


import numpy as np
import os

def gaussian_func(x, a, x0, sigma, c1, c2, c3):
    """
    Compute Gaussian function with up to quadratic background terms.

    Parameters
    ----------
    x : array
        Array of values at which to evaluate the function.
    a : float
        Height of the Gaussian function.
    x0 : float
        Location of Gaussian function.
    sigma : float
        Width of Gaussian function.
    c1 : float
        Zeroth-order background coefficient.
    c2 : float
        First-order background coefficient.
    c3 : float
        Second-order background coefficient.

    Returns
    -------
    array
        Gaussian function
    """
    return a*np.exp(-(x-x0)**2/(2*sigma**2)) + c1 + c2*x + c3*x**2

def smooth(x, window_len=11, window='hanning'):
    """
    Smooth the data using a window with requested size.

    This method is based on the convolution of a scaled window with the signal.
    The signal is prepared by introducing reflected copies of the signal
    (with the window size) at both ends so that transient parts are minimized
    in the begining and end part of the output signal.

    Parameters
    ----------
    x : array
        The input signal
    window_len : int
        The dimension of the smoothing window; should be an odd integer
    window : str
        The type of window from 'flat', 'hanning', 'hamming', 'bartlett',
        'blackman'. Flat window will produce a moving average smoothing.

    Returns
    -------
    array
        The smoothed signal
    """
    if x.ndim != 1:
        raise ValueError("smooth only accepts 1 dimension arrays.")
    if x.size < window_len:
        raise ValueError("Input vector needs to be bigger than window size.")
    if window_len<3:
        return x
    if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
        raise ValueError("Window is one of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'")

    s=np.r_[x[window_len-1:0:-1],x,x[-2:-window_len-1:-1]]

    if window == 'flat': #moving average
        w=np.ones(window_len,'d')
    else:
        w=eval('np.'+window+'(window_len)')

    y=np.convolve(w/w.sum(),s,mode='valid')

    return y[window_len//2:-window_len//2+1]

def closest(arr,val,index=False):
    """
    Find either the closest value or index of closest value in an array.

    Parameters
    ----------
    arr : array
        Array to find the closest value in.
    val : float or int
        Input value to find the closest array value to.
    index : bool, default: False
        If True, return the index of the closest value instead of the value.

    Returns
    -------
    float or int
        If `index=False`, this is the closest value
    int
        If `index=True`, returns the index of the closest value isntead,
    """
    idx = np.argmin(np.abs(arr-val))
    if index:
        return idx
    else:
        return arr[idx]


import numpy
import scipy.stats
import pandas

def compare_averages(filename):
    """
    Performs a t-test on two sets of baseball data (left-handed and right-handed hitters).

    You will be given a csv file that has three columns.  A player's
    name, handedness (L for lefthanded or R for righthanded) and their
    career batting average (called 'avg'). You can look at the csv
    file by downloading the baseball_stats file from Downloadables below.

    Write a function that will read that the csv file into a pandas data frame,
    and run Welch's t-test on the two cohorts defined by handedness.

    One cohort should be a data frame of right-handed batters. And the other
    cohort should be a data frame of left-handed batters.

    We have included the scipy.stats library to help you write
    or implement Welch's t-test:
    http://docs.scipy.org/doc/scipy/reference/stats.html

    With a significance level of 95%, if there is no difference
    between the two cohorts, return a tuple consisting of
    True, and then the tuple returned by scipy.stats.ttest.

    If there is a difference, return a tuple consisting of
    False, and then the tuple returned by scipy.stats.ttest.

    For example, the tuple that you return may look like:
    (True, (9.93570222, 0.000023))
    """
    df = pandas.read_csv(filename)
    leftHanded  = df[df['handedness'] == 'L']
    rightHanded = df[df['handedness'] == 'R']

    t, p = scipy.stats.ttest_ind(leftHanded['avg'], rightHanded['avg'], equal_var = False)
    if p < 0.05:
        return (False, (t,p))
    else:
        return (True, (t,p))

print compare_averages('baseball_stats.csv')


import numpy as np
import matplotlib.pylab as plt
import scipy
import scipy.linalg
import sys

def solve_triangular(A, b):
    n = len(b)
    x = np.empty_like(b)
    for i in range(n-1, -1, -1):
        x[i] = b[i]
        for j in range(n-1, i, -1):
            x[i] -= A[i, j] * x[j]
        x[i] /= A[i, i]
    return x

def gaussian_elimination(A):
    m, n = A.shape

    # initialise the pivot row and column
    h = 0
    k = 0
    while h < m and k < n:
        # Find the k-th pivot:
        i_max = np.argmax(A[h:, k]) + h
        if A[i_max, k] == 0:
            # No pivot in this column, pass to next column
            k = k+1
        else:
            # swap rows
            A[[h, i_max], :] = A[[i_max, h], :]
            # Do for all rows below pivot:
            for i in range(h+1, m):
                f = A[i, k] / A[h, k]
                # Fill with zeros the lower part of pivot column:
                A[i, k] = 0
                # Do for all remaining elements in current row:
                for j in range(k + 1, n):
                    A[i, j] = A[i, j] - A[h, j] * f
            # Increase pivot row and column
            h = h + 1
            k = k + 1
    return A

def solve_gaussian_elimination(A, b):
    augmented_system = np.concatenate((A, b.reshape(-1, 1)), axis=1)
    gaussian_elimination(augmented_system)
    return solve_triangular(augmented_system[:, :-1], augmented_system[:, -1])

def random_matrix(n):
    R = np.random.rand(n, n)
    A = np.zeros((n, n))
    triu = np.triu_indices(n)
    A[triu] = R[triu]
    return A

def random_non_singular_matrix(n):
    A = np.random.rand(n, n)
    while np.linalg.cond(A) > 1/sys.float_info.epsilon:
        A = np.random.rand(n, n)
    return A

As = [
    np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]),
    random_non_singular_matrix(3),
    random_non_singular_matrix(4),
    random_non_singular_matrix(5),
    random_non_singular_matrix(6),
]

bs = [
    np.array([1, 2, 3]),
    np.random.rand(3),
    np.random.rand(4),
    np.random.rand(5),
    np.random.rand(6),
]

for A, b in zip(As, bs):
    x_scipy = scipy.linalg.solve(A, b)
    x_mine = solve_gaussian_elimination(A, b)
    np.testing.assert_almost_equal(x_scipy, x_mine)

A = np.array([
    [4.5, 3.1],
    [1.6, 1.1],
])
x1 = scipy.linalg.solve(A, np.array([19.249, 6.843]))
x2 = scipy.linalg.solve(A, np.array([19.25, 6.84]))
print('percentage error is ', np.linalg.norm(x2 - x1) / np.linalg.norm(x1) * 100)
print('condition number is ', np.linalg.cond(A))


import matplotlib.pyplot as plt
import numpy as np

"""Question-2:: Gradient Decent Method"""

def cost_function(cf):
    cf = np.asarray(cf)
    cf_x = cf[0]
    cf_y = cf[1]
    cost = np.log(1 - cf_x - cf_y) - np.log(cf_x) - np.log(cf_y)
    return cost

def gradient_cost_function(gcf):
    gcf = np.asarray(gcf)
    gcf_x = gcf[0]
    gcf_y = gcf[1]
    gradient_x = np.divide(1, (1 - gcf_x - gcf_y)) - np.divide(1, gcf_x)
    gradient_y = np.divide(1, (1 - gcf_x - gcf_y)) - np.divide(1, gcf_y)
    new_gcf = np.asarray((gradient_x, gradient_y))
    return new_gcf

"""weights initialization"""

w_x = np.random.uniform(0, 0.5, 1)
w_y = np.random.uniform(0, 0.4, 1)
weights = np.asarray((w_x, w_y))
print weights

learning_rate = 0.0001
threshold = 0.00000001
iteration_number = 0
domain_miss = 0
actual_cost = [[0.0]]
actual_cost = np.asarray(actual_cost, dtype=np.float)
cost_plot = []
iteration_plot = []
w0_plot = []
w1_plot = []
w0_plot.append(weights[[0]])
w1_plot.append(weights[[0]])

while True:
    step_update = gradient_cost_function(weights)
    temp_weights = weights - learning_rate * step_update
    if ((temp_weights[[0]] + temp_weights[[1]]) < 1) and (temp_weights[[0]] > 0) and (temp_weights[[1]] > 0):
        weights = temp_weights
        w0_plot.append(temp_weights[[0]])
        w1_plot.append(temp_weights[[1]])
        temp_cost = cost_function(weights)
        iteration_temp = iteration_number + 1
        iteration_plot.append(iteration_temp)
        cost_plot.append(temp_cost)
    else:
        w_x = np.random.uniform(0, 0.5, 1)
        w_y = np.random.uniform(0, 0.4, 1)
        weights = np.asarray((w_x, w_y))
        domain_miss = domain_miss + 1
        print "number of domain misses:", domain_miss, "weights are:", temp_weights
        continue
    iteration_number = iteration_number + 1
    actual_cost_temp = cost_function(weights)
    actual_cost = np.append(actual_cost, actual_cost_temp)
    threshold_cal = np.subtract(actual_cost[iteration_number], actual_cost[iteration_number - 1])
    print "iteration number::", iteration_number, "cost difference", threshold_cal, "cost::", actual_cost_temp
    if np.absolute(threshold_cal) < threshold:
        break

print "final weights::", weights
print "final cost::", cost_function(weights)
plt.figure(1)
plt.xlabel('iteration number')
plt.ylabel('cost')
plt.scatter(iteration_plot, cost_plot, color='red', s=4)
plt.savefig('question2GD.png')
plt.figure(2)
plt.scatter(w0_plot, w1_plot, s=2)
plt.xlabel('x')
plt.ylabel('y')
plt.savefig('Q2pointPlotGD.png')


import scipy as sp
from scipy import stats as stats
from scipy import linalg as la

def posterior(prior,depthProbs,x,y):
	posterior = sp.zeros((20,20))
	for i in xrange(20):
		for j in xrange(20):
			posterior[i,j] = prior[i,j]/(1-prior[i,j]*depthProbs[i,j])
	posterior[x,y] = (prior[x,y]*(1-depthProbs[x,y]))/((1-prior[x,y]) + prior[x,y]*(1-depthProbs[x,y]))
	return(posterior/sp.sum(posterior))

def nextTarget(p):
	ind = sp.argmax(p)
	x = ind/20
	y = ind%20
	return x,y

def search(location,prior,depthProbs):
	found = False
	n_iters = 0
	while found == False:
		n_iters += 1
		x,y = nextTarget(prior*depthProbs)
		if (x==location[0] and y==location[1]):
			success = stats.bernoulli.rvs(depthProbs[x,y])
			if success:
				found = True
		prior = posterior(prior,depthProbs,x,y)
	return n_iters,sp.array([x,y])

def searchSimulation(location,prior,depthProbs,n_sims):
	searches = sp.zeros(n_sims)
	for i in xrange(n_sims):
		searches[i] = search(location,prior,depthProbs)[0]

	print "Shortest Search Length: {}".format(searches.min())
	print "Longest Search Length: {}".format(searches.max())
	print "Average Search Length: {}".format(sp.mean(searches))
	return searches


import numpy as np

# define a rotation matrix. rotate 45 degree by z axis.

R = np.array([
    [np.sqrt(2) / 2, -np.sqrt(2) / 2, 0.],
    [np.sqrt(2) / 2, np.sqrt(2) / 2, 0.],
    [0., 0., 1.]]
)

# calculate the axis and angle.

theta = np.arccos((R.trace() - 1) / 2) # rotation angle

# the rotation axis(column vector) is the eig vector corresponding to the eig value equals 1.
eig_vals, eig_vector = np.linalg.eig(R)
print('eig values:\n', eig_vals.real)
print('eig vector:\n', eig_vector.real)

n = eig_vector[eig_vals == 1].real.T
print('rotation angle:theta=', np.rad2deg(theta), 'rotation axis:n=', n.ravel())
print('i.e: we can find this R is rotating 45 degree by Z axis.')

# Rodrigue's formula: convert Axis-angle to Rotation matrix.

def skew_symmetric(a):
    a = a.ravel()
    return np.array([
        [0, -a[2], a[1]],
        [a[2], 0, -a[0]],
        [-a[1], a[0], 0]
    ])

R_ = np.cos(theta) * np.eye(3) + (1 - np.cos(theta)) * n @ n.T + np.sin(theta) * skew_symmetric(n)
print('restored rotation matrix:\n',R_)
print('check by mse:', np.linalg.norm(R - R_))


import numpy as np

#  compute f
def f(A,b,x):
    fx = np.zeros(A.shape[0])
    for i in range(A.shape[0]):
        ci = b[i]*np.dot(A[i],x)
        fx[i] = np.log(1+np.exp(-ci))
    fx = fx.mean()
    return fx

# compute gradient
def stogradfx(A,b,x, i):
    di = np.exp(-np.dot(b[i], np.dot(A[i],x)))
    gradfx = (-b[i] * A[i] * di / (1+di)).T
    return gradfx

def gradfx(A,b,x):
    n = A.shape[0]
    gradfx = np.zeros(x.shape)
    for i in range(n):
        gradfx += stogradfx(A,b,x,i)
    gradfx = gradfx/n

    return gradfx

def Oracles(b, A, lbd):
    """
    FIRST ORDER ORACLE
    Takes inputs b, A, lbd and returns two anonymous functions, one for
    the objective evaluation and the other for the gradient.
    fx(x) computes the objective (l-2 regularized) of input x
    gradf(x) computes the gradient (l-2 regularized) of input x
    gradfsto(x,i) computes the stochastic gradient (l-2 regularized) of input x at index i
    """
    n, p = A.shape
    fx  = lambda x : 0.5*lbd*np.linalg.norm(x, 2)**2 + f(A,b,x)
    gradf  = lambda x: lbd*x + gradfx(A,b,x)
    gradfsto = lambda x, i: lbd * x + stogradfx(A, b, x, i)
    return fx, gradf, gradfsto

def compute_error(A_test,b_test,x):
    n, err = A_test.shape[0], 0
    for i in range(n):
        if np.dot(b_test[i],np.dot(A_test[i],x)) <= 0:
           err += 1
    err = err/float(n)
    return err


import autograd.numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize, shgo
from autograd import grad

def convex_function(x):
    return np.sum(np.array(x)**2, axis=0)

def rosenbrock(x):
    a = 1.0
    b = 100.0
    return (a-x[0])**2 + b*(x[1]-x[0]**2)**2

def rastrigin(x):
    A = 10.0
    n = len(x)
    ret = A*n
    for i in range(n):
        ret += x[i]**2 - A * np.cos(2*np.pi*x[i])
    return ret

def visualise_functions():
    nx, ny = (100, 100)
    x = np.linspace(-5, 5, nx)
    y = np.linspace(-5, 5, ny)
    xv, yv = np.meshgrid(x, y)
    eval_convex = convex([xv, yv])
    eval_rastrigin = rastrigin([xv, yv])
    eval_rosenbrock = rosenbrock([xv, yv])

    plt.contourf(x, y, eval_convex)
    plt.colorbar()
    plt.show()
    plt.clf()
    plt.contourf(x, y, eval_rosenbrock)
    plt.colorbar()
    plt.show()
    plt.clf()
    plt.contourf(x, y, eval_rastrigin)
    plt.colorbar()
    plt.show()

def optimize(function, method, autodiff):
    eval_points_x = []
    eval_points_y = []
    def fill_eval_points(xk):
        eval_points_x.append(xk[0])
        eval_points_y.append(xk[1])

    x0 = np.array([2.5, 2.5])
    if autodiff:
        jac = grad(function)
    else:
        jac = None

    if method == 'shgo':
        bounds = [(-10, 10), (-10.0, 10.0)]
        res = shgo(function, bounds, callback=fill_eval_points,
                    options={'disp': True})
    else:
        res = minimize(function, x0, method=method, callback=fill_eval_points,
                        jac = jac,
                    options={'disp': True})

    nx, ny = (100, 100)
    x = np.linspace(-5, 5, nx)
    y = np.linspace(-5, 5, ny)
    xv, yv = np.meshgrid(x, y)
    eval_function = function([xv, yv])

    print(function.__name__)
    plt.clf()
    plt.contourf(x, y, eval_function)
    plt.plot(eval_points_x, eval_points_y, 'x-k')
    plt.colorbar()
    neval = res.nfev
    try:
        neval += res.njev
    except:
        print('no njev')
    try:
        neval += res.nhev
    except:
        print('no nhev')
    plt.title('iterations: {} evaluations: {}'.format(res.nit, neval))

    if autodiff:
        ext = '-auto.pdf'
    else:
        ext = '.pdf'

    plt.savefig(function.__name__ + '-' + method + ext)

if __name__ == '__main__':
    for f in [convex_function, rosenbrock, rastrigin]:
        for m in ['shgo','nelder-mead', 'cg', 'bfgs', 'newton-cg']:
            for a in [False, True]:
                if m == 'newton-cg' and a == False:
                    continue
                if m == 'shgo' and a == True:
                    continue
                optimize(f, m, a)


""" transfor data vectors into their Fourier coefficients
    we will approximate derivaties by taking advantage of FFT

    1st - take a function that we can analytically compute the exact derivative
    2nd - we are going to compare how accurate FFT is compared to analytic derivative
    and we also compare with simple finite difference derivative """

import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = [12, 12]
plt.rcParams.update({'font.size': 10})

n = 64
L = 30                      # the length of our domain is gonna be 30
dx = L / n
x = np.arange(-L/2, L/2, dx, dtype ='complex_')
f = np.cos(x) * np.exp(-np.power(x, 2) / 25)                            # Function
    # cosine * decaying Gaussian function = Cosine in a Gaussian envelope -- use chain rule to get exact derivative
df = -(np.sin(x) * np.exp(-np.power(x, 2) / 25) + (2 / 25) * x * f)     # Derivative

""" Approximate derivative using Finite Difference -- it is really crude derivative to be honest (fk+1 - fk) / deltax
    this is not a good approximation of the derivative has error that scales like Delta X order Delta X error
    better job would be Central Difference or a Higher Order finite difference -- but this is just illustration """
dfFD = np.zeros(len(df), dtype='complex_')
for kappa in range(len(df) - 1):
    dfFD[kappa] = (f[kappa + 1] - f[kappa]) / dx

dfFD[-1] = dfFD[-2]

""" Derivative using FFT (spectral derivative)        = i * W * fhat = i * K * fhat
    use kappa when fourier transforming in space      K - spatial frequencies (wave numbers)
    use omega when fourier transforming in time       W - temporal frequencies
    fhat is vector of fourier coefficients            and Kappa is a vector of frequencies
    frequency weighted fourier coefficients * i --- inverse fourier this and recover the derivative of data
    at those discrete sample points """
fhat = np.fft.fft(f)
kappa = (2 * np.pi / L) * np.arange(-n/2, n/2)
kappa = np.fft.fftshift(kappa)                          # re-order fft frequencies
dfhat = kappa * fhat * (1j)
dfFFT = np.real(np.fft.ifft(dfhat))

""" Up shot here: All of these steps is very fast and very accurate:
    FFT = O(n*log(n))
    - create kappa vector
    - compute derivative = i * kappa * f
    - inverse fourier transform to get the derivative back in spatial units
    and  because these are complex numbers in fhat when multiply out and inverse fourier transform
    we might have very very small machine precision imaginary parts that why we are just going to take
    the real part of this inverse fourier transform -- just being careful """

# Plots
plt.plot(x, df.real, color='k', LineWidth=2, label='True Derivative')
plt.plot(x, dfFD.real, '--', color='y', LineWidth=1.5, label='Finite Difference')
plt.plot(x, dfFFT.real, '--', color='r', LineWidth=1.5, label='FFT Derivative')
plt.legend()
plt.show()

""" As you increase the data points(increase n) in signal which is decreasing deltax
    - Finite Difference Derivative does get more accurate but very slowly
    whereas our Spectral Derivative (FFT Derivative) gets more accurate very rapidly """

""" Conclusion FFT is great for Spectral Derivatives of smooth functions whose derivatives are continuous
    or else we will get Gibbs phenomenon """


# numpy provides function to perform log at base 2, e and 10.
# how we can take log for any base by creating a custom ufunc.
# all of logs function will place -inf or inf in elements if log cannot not be computed.
# log at base 2 - use log2() function to perform log at base 2
import numpy as np
from math import log

arr1 = np.arange(1, 10)           # returns array with 1 to 9 numbers
arr2 = np.log2(arr1)              # returns array with 2^? for 1 to 9 numbers
print(arr2)

arr3 = np.arange(10, 21)
arr4 = np.log10(arr3)             # returns array with 10^? for 10 to 20 numbers
print(arr4)

# natural log or log at base e - use log() function to perform log at base e.
arr5 = np.arange(30, 61)
arr6 = np.log(arr5)                     # here base is "e" i.e. 2.71828183
print(arr6)

# numpy does not provide any function to take log at any base
# so we can use frompyfunc() function along with inbuilt function math.log() with two input parameters
nplog = np.frompyfunc(log, 2, 1)     # function to be done = log, 2 is total inputs, 1 is total output
omk = nplog(100, 15)                 # log 100 with base 15
print("log at any base")
print(omk)


import numpy as np
from matplotlib import pyplot as plt

x0 = 0
y0 = 2
xf = 1
n = 11
deltax = (xf-x0)/(n-1)
x = np.linspace(x0,xf,n)
def f(x,y):
	return y-x

y = np.zeros([n])
y[0] = y0
for i in range(1,n):
	k1 = deltax*f(x[i-1],y0)
	k2 = deltax*f(x[i-1]+deltax/2,y0+k1/2)
	k3 = deltax*f(x[i-1]+deltax/2,y0+k2/2)
	k4 = deltax*f(x[i-1]+deltax,y0+k3)
	y[i] =  y0 + (k1 + 2*k2 + 2*k3 + k4)/6
	y0 = y[i]

print("x_n\t    y_n")
for i in range(n):
	print(x[i],"\t",format(y[i],'6f'))

plt.plot(x,y,'o')
plt.xlabel("Value of x")
plt.ylabel("Value of y")
plt.title("Approximation Solution with Runge-Kutta Method")
plt.show()

#table 19.3


## Week 5: Probability and the Media
# Birthday Problem
# https://www.washingtonpost.com/news/wonk/wp/2016/11/02/why-lifes-strangest-coincidences-really-arent-that-strange-at-all/?utm_term=.de47719120dd
# at what point would it be better than 50/50 that 2 people share a birthday.
# They found that when there are 23 people, you have 253 potential pairs and >50% chance that one of those pairs is a match.
# To get 253 = 23! / ((23-2)! * 2!)

# Attempt to replicate in 2 methods

import numpy as np
import scipy.misc as sm
import plotly.plotly as py
import plotly.graph_objs as go

# number of guests at party
# n == 0 & n == 1 are not parties!
n = np.array([range(2,100,1)])

# Attempt 1
y  = []
y2 = []
for i in range(2,100,1):
    days = 365
    prob = 1
    for j in range(1, (i + 1), 1):
        days -=1
        prob *= days/365
    y.append(prob)
    y2.append(1 - prob)

match = go.Scatter(x = n[0], y = y2)
no_match = go.Scatter(x = n[0], y = y)
data = [match, no_match]
plot_url = py.plot(data, filename='basic-line')

better_than_50 = min(i for i in y2 if i > 0.5)
print("Party size to have >50% chance of a shared birthday =",
      y2.index(better_than_50) + 3)
# Party size to have >50% chance of a shared birthday = 23

# Attempt 2 - simplify
# Total possible number of matches
y = sm.factorial(n) / (sm.factorial(n - 2) * sm.factorial(2))

# Chance of nobody matching
a = (364/365)**y
# Chance there is a match
b = 1 - a

match    = go.Scatter(x = n[0], y = b[0])
no_match = go.Scatter(x = n[0], y = a[0])
data = [match, no_match]
plot_url = py.plot(data, filename='basic-line')

better_than_50 = min(i for i in b[0] if i > 0.5)
print("Party size to have >50% chance of a shared birthday =",
      int(np.where(b[0] == better_than_50)[0]) + 2)
#  Party size to have >50% chance of a shared birthday = 23


from sympy import ( symbols, solve, diff, integrate, exp, sqrt, lambdify, pprint, Integral )
import matplotlib.pyplot as plt
import numpy as np

# The number of bachelor's degrees conferred has been increasing steadily in recent decades.
# The rate of change of the number of bachelor's degrees (in thousands) can be approximated by the following function where t is the number of years since 1970.

t = symbols( 't' )
dB = 0.0651 * t**2 - 1.939 *t + 16.31

# Divide by 1000
y_min = 833300 / 1000

B = Integral( dB, t ).doit() + y_min

B.subs( { t: 0 } ) == y_min # Verify

# Find Upper B(t), given that about 819,900 degrees were conferred in 1970 (t=0).
pprint( B )

# Use the formula from part a. to project the number of bachelor's degrees that will be conferred in 2010, t = 40
year_tofind = 40

deg_2010 = B.subs( { t: year_tofind } )
degrees = round( deg_2010 * 1000 )
degrees

# What does the degree distribution look like?

g_xlim = [ 0, 50 ]

lam_p = lambdify( t, B, np )
x_vals = np.linspace( g_xlim[0], g_xlim[1], 1000, endpoint = True )
y_vals = lam_p( x_vals )
plt.plot( x_vals, y_vals )
x_min, x_max = 0, year_tofind

plt.vlines( x = x_min, ymin = y_min, ymax = B.subs( { t: x_min } ), color = 'Black', zorder = 1 )
plt.vlines( x = x_max, ymin = y_min, ymax = B.subs( { t: x_max } ), color = 'Red', zorder = 1 )
plt.text( x = x_max, y= deg_2010, s = str( degrees ) )

plt.title( str( B ) )
plt.show()


# Softmax function : normalized exponential function
# Copyright 2018 Denis Rothman MIT License. See LICENSE.

import math
import numpy as np

# y is the vector of the scores of the lv vector in the warehouse example.
y = [0.0002, 0.2, 0.9,0.0001,0.4,0.6]
print('0.Vector to be normalized',y)

#Version 1 : Explicitly writing the softmax function for this case
y_exp = [math.exp(i) for i in y]
print("1",[i for i in y_exp])
print("2",[round(i, 2) for i in y_exp])
sum_exp_yi = sum(y_exp)
print("3",round(sum_exp_yi, 2))
print("4",[round(i) for i in y_exp])
softmax = [round(i / sum_exp_yi, 3) for i in y_exp]
print("5,",softmax)

#Version 2 : Explicitly but with no comments
y_exp = [math.exp(i) for i in y]
sum_exp_yi = sum(y_exp)
softmax = [round(i / sum_exp_yi, 3) for i in y_exp]
print("6, Normalized vector",softmax)

#Version 3: Using a function in a 2 line code instead of 3 lines
def softmax(x):
    return np.exp(x) / np.sum(np.exp(x), axis=0)

print("7A Normalized vector",softmax(y))
print("7B Sum of the normalize vector",sum(softmax(y)))

ohot=max(softmax(y))
ohotv=softmax(y)

print("7C.Finding the highest value in the normalized y vector : ",ohot)
print("7D.For One-Hot function, the highest value will be rounded to 1 then set all the other values of the vector to 0: ")
for onehot in range(6):
    if(ohotv[onehot]<ohot):
        ohotv[onehot]=0
    if(ohotv[onehot]>=ohot):
        ohotv[onehot]=1
print("This is a vector that is an output of a one-hot function on a softmax vector")
print(ohotv)


import numpy as np
from scipy import optimize,integrate,interpolate
import matplotlib.pyplot as plt

A = np.array([[1.,2.],[4.,5.]])
print('A =\n',A,'\n')
print('norm(A) =',np.linalg.norm(A),'\n')

B = np.ones((2,2))
C = np.zeros(B.shape)
D = np.eye(4)
E = np.diag(range(3))
b = B[:,1]

#solve A*x=b
x0 = np.linalg.inv(A)@b
x1 = np.linalg.pinv(A)@b
x2 = np.linalg.solve(A,b)
x3,residuals,rank,s = np.linalg.lstsq(A,b)

for i,x in enumerate([x0,x1,x2,x3]):
    print('x{} = {}'.format(i,x))

u,s,vh = np.linalg.svd(A)
L = np.linalg.cholesky(D)
w,v = np.linalg.eig(A)
q,r = np.linalg.qr(A)

signal = np.sin(np.linspace(0,3*np.pi,100))
out = np.fft.fft(signal)

plt.subplot(1,2,1)
plt.plot(abs(out))
plt.title('modulo')
plt.subplot(1,2,2)
plt.plot(np.angle(out))
plt.title('fase')

y,abserr = integrate.quad(lambda x: np.sqrt(1-x**2), -1, 1)

res = optimize.minimize(lambda x: np.exp(x)+x**2,
                  x0=1,
                  method='BFGS',
                  tol = 0.6)

x = np.linspace(-np.pi,np.pi,4)
y = np.sin(x)
x_new = np.linspace(-np.pi,np.pi,100)
y_new = interpolate.interp1d(x, y,'cubic')(x_new)
plt.figure()
plt.plot(x, y, 'o',
         x_new, y_new, '-',
         x_new, np.sin(x_new), '--')

sol = integrate.solve_ivp(fun = lambda t,y: -0.5*y,
                          t_span = [0, 10],
                          y0 = [5,4,2,1,3],
                          method = 'RK45',
                          max_step = 0.1)
plt.figure()
plt.plot(sol.t,sol.y.T)

plt.figure()
plt.bar(['um','dois','tres'],[4,5,6])

x = np.arange(0,4,0.1)
y = 2*x+np.random.randn(*x.shape)
plt.figure()
plt.scatter(x,y,marker='*')

N = 100
x = np.linspace(-3.0, 3.0, N)
y = np.linspace(-2.0, 2.0, N)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
plt.figure()
plt.contourf(X,Y,Z)
plt.figure()
plt.contour(X,Y,Z)
plt.figure()
plt.imshow(Z)

plt.figure()
plt.pie([3,4,2,5])

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z,cmap=cm.rainbow)

"""
Muitas outras referencias de gráficos em
matplotlib.org/gallery/index.html
"""


import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import quad
from scipy.integrate import romberg

# part a

x = np.linspace(0,5,100)

Y1 = (x**(1))*(np.exp(-x)) # a = 2
Y2 = (x**(2))*(np.exp(-x)) # a = 3
Y3 = (x**(3))*(np.exp(-x)) # a = 4

plt.plot(x,Y1,'r--',label='a = 2') # first line where a = 2 is red dashes
plt.plot(x,Y2,'b--',label='a = 3') # second line where a = 3 is blue dashes
plt.plot(x,Y3,'g--',label='a = 4') # third line where a = 4 is green dashes
plt.legend(loc=0) # adding a legend
plt.title('Gamma function with varying values of a')
plt.xlabel('x')
plt.ylabel('Gamma function Y')
plt.show()

# part b

# gamma function integrand (denoted as y for this derivation):

# y = x^(a-1) * e^(-x) where a = real constant
# y' = dy/dx = d/dx (u*v)
# when u = x^(a-1) and u' = (a-1)*x^(a-2)
# and v = e^(-x) and v' = -e^(-x)

# d/dx (u*v) = u'v+uv'

# u'v+uv' = [e^(-x)]*[(a-1)*x^(a-2)]+[x^(a-1)]*[-e^(-x)]
# = [e^(-x)]*[(a-1)*x^(a-2)]-[x^(a-1)]*[e^(-x)]
# = [e^(-x)]*[(a-1)*x^(a-2)-x^(a-1)]

# plug in x = a-1

# u'v+uv' = [e^(1-a)]*[(a-1)*(a-1)^(a-2)-(a-1)^(a-1)]
# u'v+uv' = [e^(1-a)]*[(a-1)^(a-1)-(a-1)^(a-1)] = 0

# derivative of a f(x) equals zero when x = stationary point
# so, x = a-1 gives stationary point (maximum)

# part c

def Y(x): # defining gamma quation
    return (x**(0.5))*(np.exp(-x)) #  here, a = 3/2

print "The value of the Gamma function with its error is",quad(Y, 0, np.inf),"at a = 3/2 when we use the built in sci.integrate.quad module. This value is supposed to be around 1/2*(sqrt(pi) which is ~0.886 so this value is good."

print "The value of the Gamma function  is",romberg(Y,0,1e+1),"at a = 3/2 when we use the Romberg method. This value is also around the accepted value of ~0.886 so this value is also good."

ExamPrep/Shit Comp/SciComp Exam 2016/Assignments 15-16/A2Q4 quad romberg scipy integrate.py


# one stock -> simple return is common but not for many
# $$
# \frac{P_1 - P_0}{P_0} = \frac{P_1}{P_0} - 1
# $$
import numpy as np
import pandas as pd
from pandas_datareader import data as wb
import matplotlib.pyplot as plt

# In[2]:
PG = wb.DataReader('PG', data_source='yahoo', start='1995-1-1')
# with iex key
PG = wb.DataReader('PG', data_source='iex', start='2015-1-1')
# csv
PG = pd.read_csv('Section-11_PG_1995-03_23_2017.csv')
PG = PG.set_index('Date')
PG.head()
PG.tail()
PG['simple_return'] = (PG['Adj Close'] / PG['Adj Close'].shift(1)) - 1
print(PG['simple_return'])
#
tickers = ['PG', 'MSFT', 'T', 'F', 'GE']
newDataFrame = pd.DataFrame()
for t in tickers:
    newDataFrame[t] = wb.DataReader(t, data_source='iex', start='2015-1-1')['close']
newDataFrame.tail()
newDataFrame.head()
newDataFrame.to_csv('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-example_01.csv')
newDataFrame.to_excel('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-example_01.xlsx')
#
import quandl
#
mydata_01 = quandl.get("FRED/GDP")
mydata_01.tail()
mydata_01.head()
mydata_01.to_csv('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-Data_01.csv')
mydata_01 = pd.read_csv('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-Data_01.csv', index_col='Date')
mydata_01.tail()
mydata_01 = pd.read_csv('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-Data_01.csv')
mydata_01.tail()
mydata_01.set_index('Date')
mydata_01.tail()
mydata_02 = pd.read_csv('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-Data_02.csv', index_col='Date')
mydata_02.head()
mydata_02.tail()
mydata_02.to_excel('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-Data_02.xlsx')
mydata_02.info()
mydata_03 = pd.read_excel('Section-10_57-ImportingandOrganizingYourDatainPython-PartIII-Data_03.xlsx')
mydata_03.info()
mydata_03.set_index('Year')
mydata_03.info()


import numpy as np

# Write a function that takes as input a list of numbers, and returns
# the list of values given by the softmax function.

# This implementation supports L of any dimensionality
def softmax(L): # Let L = [5, 6, 7]
    # View inputs as arrays with atleast two dimensions
    y = np.atleast_2d(L) # [[ 5, 6, 7 ]]

    # Find axis along which to do computation
    # axis = 0 means do computation row wise
    # axis = 1 means do computation column wise
    axis = next(j[0] for j in enumerate(y.shape) if j[1] > 1) # axis = 1

    # Calculate maximum value in given list, i.e, max(L)
    y_max = np.max(y, axis = axis) # [7]

    # Step 1: Calculate exp(Ln - max(L)) which is equivalent to exp(Ln) / exp(max(L))
    # (i) Ln - max(L)
    # Expand dimensions of y_max along axis = 1 in our case and subtract from y
    y = y - np.expand_dims(y_max, axis) # [[5, 6, 7]] - [[7]] = [[-2,-1,0]]
    # (ii) exp(Ln - max(L))
    y = np.exp(y) # [[0.13533528 0.36787944 1.        ]]

    # Step 2: Calculate sum(exp(Ln - max(L))) which is equivalent to sum(exp(Ln)) / exp(max(L))
    y_sum = np.sum(y, axis = axis) # Add along axis = 1 in our case => [1.50321472]
    y_sum_expanded = np.expand_dims(y_sum, axis) # [[1.50321472]]

    # Step 3: Calculate exp(Ln) / sum(exp(Ln)) which can be obtained by dividing result of Step 1 by result of Step 2
    p = y / y_sum_expanded # [[0.09003057 0.24472847 0.66524096]]

    return p.flatten() # Return array of copy collapsed to one dimension

    pass


import numpy as np

def hypothesis(b,m,x):
	predicted_y = (b + (m*x))
	return predicted_y

def cost(b,m,points):
	totalError = 0
	M = (float(len(points)))
	for i in range(len(points)):
		x = float(points[i,0])
		y = float(points[i,1])
		totalError += (1/(2*M)) * ((hypothesis(b,m,x) - y)**2)
	return totalError

def gradient_descent(b,m,points,alpha,epochs):
	partial_b=partial_m=0
	new_b = b
	new_m = m
	for convergence in range(epochs):
		for i in range(len(points)):
			x = float(points[i,0])
			y = float(points[i,1])
			partial_b += (1/float(len(points))) * (hypothesis(new_b,new_m,x) - y)
			partial_m += (1/float(len(points))) * (hypothesis(new_b,new_m,x) - y) * x
		new_b = (b - (alpha*partial_b))
		new_m = (m - (alpha*partial_m))
	return [new_b, new_m]

def main():
	b=m=0
	alpha = 0.0008
	epochs = 1000
	points = np.genfromtxt('data.csv', delimiter=',')
	[new_b, new_m] = gradient_descent(b, m, np.asarray(points), alpha, epochs)
	x = float(input("Enter X value to predict : "))
	y = hypothesis(new_b,new_m,x)
	print('Predicted price is : {}'.format(y))

	print("Cost function's weights are b:{}, m:{}".format(new_b, new_m))
	print("Total cost is : {}".format(cost(new_b,new_m,np.asarray(points))))

if __name__ == '__main__':
	main()


import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
np.random.seed(0)

#%% https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.norm.html
# Generate the sample
mu = 170
sd = 8
x = norm.rvs(loc=mu, scale=sd, size=100)

#%% Statistics
print(x.mean())
print(x.var())
print(x.std())

#%% Delta Degrees of freedom
print(x.var(ddof=1))
print(x.std(ddof=1))

#%% Quantile function (inverse of cdf)
print(norm.ppf(0.95, loc=mu, scale=sd))

#%% CDF
print(norm.cdf(165, loc=mu, scale=sd))

#%% Survival Function = 1 - cdf
print(1-norm.cdf(165, loc=mu, scale=sd))
print(norm.sf(165, loc=mu, scale=sd))


# -*- coding: utf-8 -*-
"""
Examples of root finding

@author: Nicolas Guarin-Zapata
@date: Febr, 2018
"""
from __future__ import division, print_function
import numpy as np
import matplotlib.pyplot as plt
from roots import newton, bisection

npts = 201
a = -1.0
b = 10.0
fun = lambda x: x**3 + 4.0*x**2 - 10.0
deriv = lambda x: 3.0*x**2 + 8.0*x

#%% Plot
x = np.linspace(a, b, npts)
y = fun(x)
plt.plot(x, y)
plt.grid(True)
plt.ylim(-40, 100)
plt.xlabel("x")
plt.ylabel("y")

#%% Bisection
print("Bisection solution")
print("="*25)
bisection(fun, a, b, verbose=True)

#%% Newton-Raphson
print("\n\n")
print("Newton-Raphson solution")
print("="*25)
newton(fun, deriv, 10, verbose=True, ftol=1e-16)

plt.show()


#!/bin/env python3
# coding: utf-8

import numpy as np
import sys

def linear(x, y, n):
	n = len(x)
	b = (n*sum(x*y)-sum(x)*sum(y))/(n*sum(x*x)-sum(x)**2)
	a = (sum(y)-b*sum(x))/n

	return a, b

def R2(x, y, n, a, b, c = 0):
	yMean = sum(y) / n
	yHat = a + b * x + c * x*x

	sst = sum((y - yMean) ** 2)
	sse = sum((yHat - yMean) ** 2)
	ssr = sum((y - yHat) ** 2)

	r2 = sse / sst
	r2_adj = 1 - (n - 1) / (n - 2) * (1 - r2)

	return r2 * 100 , r2_adj * 100

def main():
	x = np.array([0.0162, 1.4094, 3.0132, 5.5080, 8.1000, 10.3032, 11.8422])
	y = np.array([0.0089, 0.0265, 0.0400, 0.0650, 0.0835, 0.1017, 0.1092])

	if len(x) != len(y):
		sys.exit("Number of data points for x and y are different!")

	n = len(x)
	a, b = linear(x, y, n)
	r2, r2_adj = R2(x, y, n, a, b)

	print("Linear Regression")
	print("-----------------")
	print("")
	print("y = {:.10f} + {:.10f} x".format(a, b))
	print("")
	print("R² = {:.2f}%".format(r2))
	print("R²_adj = {:.2f}%".format(r2_adj))

if __name__ == '__main__':
	main()


import numpy as np
import pandas as pd

# peason
def Peason(x,y):
    x = np.squeeze(x)
    y = np.squeeze(y)
    x = x - x.mean()
    y = y - y.mean()
    return x.dot(y)/(np.linalg.norm(x)*np.linalg.norm(y))
# Jaccard
def Jaccard(x,y):
    x = np.squeeze(x)
    y = np.squeeze(y)
    a = x.dot(y)
    b = np.linalg.norm(x)
    c = np.linalg.norm(y)
    return a/(np.power(b,2)+np.power(c,2)-a)
# cosine
def Cosine(x,y):
    x = np.squeeze(x)
    y = np.squeeze(y)
    return x.dot(y)/(np.linalg.norm(x)*np.linalg.norm(y))
def split_train_test(root_file,raw=True,percemtage = 0.75):
    outdata = pd.read_excel(root_file,sheet_name="train")
    names = outdata.iloc[:, 0]
    y_value = outdata.iloc[:, 2]
    x_value = outdata.iloc[:, 3:]
    if raw:
        return x_value.values,y_value.values[:, np.newaxis]
    else:
        length = len(outdata)
        train_len = int(percemtage*length)
        train_x_names = names[:train_len]
        train_x = x_value[:train_len]
        train_y = y_value[:train_len]

        test_x_names = names[train_len:]
        test_x = x_value[train_len:]
        test_y = y_value[train_len:]
        return (train_x.values,train_y.values[:, np.newaxis]),\
               (test_x.values,test_y.values[:, np.newaxis])


from lagrangianpoly import LagrangianBasis, LagrangianPoly, Derivative
import SymbolicFiniteDifference as fd
import SymbolicInterpolation as intp
import TaylorExpansion as te

def main():
    import sympy as sp

    # lagrangian basis
    x = sp.symbols('x')
    eq = LagrangianBasis(x, degreeOfPolynomial=5, pointAt=0)
    print(eq)
    # (x - x1)*(x - x2)*(x - x3)*(x - x4)*(x - x5)/((x0 - x1)*(x0 - x2)*(x0 - x3)*(x0 - x4)*(x0 - x5))

    x = sp.symbols('x')
    dx = sp.symbols('dx')

    # 3-point stencil lagrangian polynomial
    x_set = [-dx, 0, dx]
    f_set = sp.symbols('f0:{:d}'.format(len(x_set)))
    eq = LagrangianPoly(x, x_set, f_set)
    print(eq)
    # f0*x*(-dx + x)/(2*dx**2) - f1*(-dx + x)*(dx + x)/dx**2 + f2*x*(dx + x)/(2*dx**2)

    # 3-point stencil central difference for 1st derivative on regular grid
    x_set = [-dx, 0, dx]
    f_set = sp.symbols('f0:{:d}'.format(len(x_set)))
    eq = Derivative(LagrangianPoly(x, x_set, f_set), x, orderOfDifference=1)
    print(eq)

    # 3-point stencil central difference for 2nd derivative on regular grid
    x_set = [-dx, 0, dx]
    f_set = sp.symbols('f0:{:d}'.format(len(x_set)))
    eq = Derivative(LagrangianPoly(x, x_set, f_set), x, orderOfDifference=2)
    print(eq)

    # 5-point stencil central difference for 1st derivative on regular grid
    x_set = [-2*dx, -dx, 0, dx, 2*dx]
    f_set = sp.symbols('f0:{:d}'.format(len(x_set)))
    eq = Derivative(LagrangianPoly(x, x_set, f_set), x, orderOfDifference=1)
    print(eq)

    # 3-point stencil right-sided difference for 1st derivative on regular grid
    x_set = [0, dx, 2*dx]
    f_set = sp.symbols('f0:{:d}'.format(len(x_set)))
    eq = Derivative(LagrangianPoly(x, x_set, f_set), x, orderOfDifference=1)
    print(eq)

    # 4-point stencil right-sided difference for 2nd derivative on regular grid
    x_set = [0, dx, 2*dx, 3*dx]
    f_set = sp.symbols('f0:{:d}'.format(len(x_set)))
    eq = Derivative(LagrangianPoly(x, x_set, f_set), x, orderOfDifference=2)
    print(eq)

    # 3-point stencil central difference for 1st derivative on staggered grid
    x_set = [-dx/2, 0, dx/2]
    f_set = sp.symbols('f0:{:d}'.format(len(x_set)))
    eq = Derivative(LagrangianPoly(x, x_set, f_set), x, orderOfDifference=1)
    print(eq)

    # 5-point central difference for 1st derivative with a default interval symbol
    stencil = [-2, -1, 0, 1, 2]
    eq = fd.getFiniteDifferenceEquation(
        stencil, orderOfDifference=1)
    print(eq)

    # 5-point central difference for 1st derivative with a default interval symbol
    stencil = [-2, -1, 0, 1, 2]
    eq = fd.getFiniteDifferenceEquation(
        stencil, 1, sameSubscriptsAsStencil=True, evaluate=False)
    print(eq)

    # 5-point central difference for 1sit derivative with an user-defined interval symbol
    stencil = [-2, -1, 0, 1, 2]
    eq = fd.getFiniteDifferenceEquation(
        stencil, orderOfDifference=1, intervalSymbolStr='dx')
    print(eq)

    # 3-point one-sided difference for 1st derivative with a default interval symbol
    stencil = [0, 1, 2]
    eq = fd.getFiniteDifferenceEquation(
        stencil, orderOfDifference=1)
    print(eq)

    # 4-point one-sided difference for 2nd derivative with a default interval symbol
    stencil = [0, 1, 2, 3]
    eq = fd.getFiniteDifferenceEquation(
        stencil, orderOfDifference=2)
    print(eq)

    # 5-point central difference formula for 1st derivative with changing subscripts
    stencil = [-2, -1, 0, 1, 2]
    eq = fd.getFiniteDifferenceEquation(
        stencil, orderOfDifference=1, sameSubscriptsAsStencil=True)
    print(eq)

    # 5-point central difference formula for 1st derivative with changing subscripts
    stencil = [-1.5, -0.5, 0, 0.5, 1.5]
    eq = fd.getFiniteDifferenceEquation(
        stencil, orderOfDifference=1, sameSubscriptsAsStencil=True)
    print(eq)

    # coefficients for 5-point central difference for 1st derivative
    stencil = [-2, -1, 0, 1, 2]
    coef = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=1)
    print(coef)

    # coefficients for 7-point central difference for 1st derivative
    stencil = [-3, -2, -1, 0, 1, 2, 3]
    coef = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=1)
    print(coef)

    # coefficients for 3-point one-sided difference for 1st derivative
    stencil = [0, 1, 2]
    coef = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=1)
    print(coef)

    # coefficients for 4-point one-sided difference for 2nd derivative
    stencil = [0, 1, 2, 3]
    coef = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=2)
    print(coef)

    # numerator and denominator of coefficients for 5-point central difference for 1st derivative
    stencil = [-2, -1, 0, 1, 2]
    numr, denom = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=1, as_numr_denom=True)
    print(numr, denom)

    # numerator and denominator of coefficients for 7-point central difference for 1st derivative
    stencil = [-3, -2, -1, 0, 1, 2, 3]
    numr, denom = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=1, as_numr_denom=True)
    print(numr, denom)

    # numerator and denominator of coefficients for 3-point one-sided difference for 1st derivative
    stencil = [0, 1, 2]
    numr, denom = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=1, as_numr_denom=True)
    print(numr, denom)

    # numerator and denominator of coefficients for 3-point one-sided difference for 1st derivative on staggered grid
    stencil = [-0.5, 0, 0.5]
    numr, denom = fd.getFiniteDifferenceCoefficients(
        stencil, orderOfDifference=1, as_numr_denom=True)
    print(numr, denom)

    # Tayloer expansion of f(x+h) around x up to term including 6th order difference
    h = sp.symbols('h')
    f1 = te.TaylorExpansion(h, n=6)
    print(f1)

    # Tayloer expansion of f(x-h) around x up to term including 7th order difference
    h = sp.symbols('h')
    f_1 = te.TaylorExpansion(-h, n=7)
    print(f_1)

    # Tayloer expansion of f(x+2h) around x up to term including 4th order difference
    h = sp.symbols('h')
    f2 = te.TaylorExpansion(2*h, n=4)
    print(f2)

    # truncation error of 3-point central finite difference for 1st derivative
    stencil = [-1, 0, 1]
    err = fd.getTruncationError(stencil, 1)
    print(err)

    # truncation error of 3-point central finite difference for 2nd derivative
    stencil = [-1, 0, 1]
    err = fd.getTruncationError(stencil, 2)
    print(err)

    # truncation error of 5-point central finite difference for 1st derivative
    stencil = [-2, -1, 0, 1, 2]
    err = fd.getTruncationError(stencil, 1)
    print(err)

    # truncation error of 3-point central finite difference for 1st dervative on the staggered grid
    stencil = [-0.5, 0, 0.5]
    err = fd.getTruncationError(stencil, 1)
    print(err)

    # truncation error of 3-point central finite difference for 1st derivative on the staggered grid
    stencil = [-1.5, -0.5, 0, 0.5, 1.5]
    err = fd.getTruncationError(stencil, 1)
    print(err)

    # 2-point central interpolation
    stencil = [-1, 1]
    eq = intp.getInterpolationEquation(stencil)
    print(eq)

    # 4-point central interpolation
    stencil = [-2, -1, 1, 2]
    eq = intp.getInterpolationEquation(stencil)
    print(eq)

    # coefficients for 2-point central interpolation
    stencil = [-1, 1]
    coef = intp.getInterpolationCoefficients(stencil)
    print(coef)

    # coefficients for 4-point central interpolation
    stencil = [-2, -1, 1, 2]
    coef = intp.getInterpolationCoefficients(stencil)
    print(coef)

    # numerator and denominator of coefficients for 2-point central interpolation
    stencil = [-1, 1]
    numr, denom = intp.getInterpolationCoefficients(
        stencil, as_numr_denom=True)
    print(numr, denom)

    # numerator and denominator of coefficients for 4-point central interpolation
    stencil = [-2, -1, 1, 2]
    numr, denom = intp.getInterpolationCoefficients(
        stencil, as_numr_denom=True)
    print(numr, denom)

    # truncation error of 2-point central interpolation
    stencil = [-1, 1]
    err = intp.getTruncationError(stencil)
    print(err)

    # truncation error of 4-point central interpolation
    stencil = [-2, -1, 1, 2]
    err = intp.getTruncationError(stencil)
    print(err)

    # linear extrapolation
    stencil = [1, 2]
    eq = intp.getInterpolationEquation(stencil)
    err = intp.getTruncationError(stencil)
    print(eq)
    print(err)

    # quadratic extrapolation
    stencil = [1, 2, 3]
    eq = intp.getInterpolationEquation(stencil)
    err = intp.getTruncationError(stencil)
    print(eq)
    print(err)

    return

if __name__ == "__main__":
    main()


import numpy as np

def polynomial_interpolation(x, y):
    """
        Computes c of interpolating polynomial using Newton method.

        Parameters
        ----------
        x : ndarray
            The values of the independent variable
        y : list
            The values of f(x) for every x, where x is the independent variable

        Returns
        -------
        c : list
            coefficients of interpolating polynomial
    """
    v = []
    [v.append([0 for j in range(len(x))]) for i in range(len(x))]
    for j in range(len(x)):
        v[j][0] = y[j]

    for i in range(1, len(x)):
        for j in range(len(x) - i):
            v[j][i] = (v[j + 1][i - 1] - v[j][i - 1]) / (x[j + i] - x[j])

    c = []
    for i in range(len(x)):
        c.append(v[0][i])

    return c

def calculate_polynomial(degree, x_data, coefficients, x):
    """
        Computes the value of the interpolating polynomial.

        Parameters
        ----------
        degree : int
            The degree of the interpolating polynomial.
        x_data : list
            The values that were used when calculating the c of the interpolating polynomial.
        coefficients : list
            The coefficients of the interpolating polynomial, constant term first.
        x : int
            The point at which the polynomial will be calculated

        Returns
        -------
        value : float
            The value of the interpolating polynomial at point x.
    """
    value = 0.0
    for i in range(degree + 1):
        temp = coefficients[i]
        for j in range(i):
            temp *= (x - x_data[j])
        value += temp

    return value

def custom_sin(value):
    """
        Approximates sin curve with degree 9 polynomial.

        Parameters
        ----------
        value : float
            The point at which the sin will be approximated

        Returns
        -------
        float
            The approximation value of the sin curve at point x.
    """
    x = [0.0, 0.65, 1.3, 1.9500000000000002, 2.6, 3.25, 3.9000000000000004, 4.55, 5.2, 2*np.pi]
    y = [0.0, 0.6051864057, 0.9635581854, 0.9289597150, 0.5155013718, -0.1081951345, -0.6877661591, -0.9868438585,
         -0.8834546557, 0]
    c = polynomial_interpolation(x, y)
    value = value % (2 * np.pi)

    return calculate_polynomial(len(x) - 1, x, c, value)


# MSDS 400 Module 9 Practice 1

from numpy import random, arange, array
import matplotlib.pyplot as plt

# The random sample size=nsamples and can be specified by the student.

nsamples = 100
sample = random.random(nsamples)  # This draws a random sample.

nbins = 10  # This defines the number of subintervals for the histogram.
bns = float(nbins)
'''
For a uniform distribution, the proportions in each subinterval are
expected to be the same.  With ten subintervals, this amounts to 0.1 in
each.  With 20 subintervals it amounts to 0.05.
'''
expected = 1.0 / bns  # This defines the expected subinterval proportion.

ind = arange(nbins)  # This sets ind to serve as a range of indices.
h = [0] * nbins  # This prepares h to serve as a list of the proper length.
histogram = {}  # This defines histogram as a void dictionary.

for k in ind:
    histogram[k] = 0  # This initializes the dictionary with zero counts.
'''
In the for loop we run v across all randomly generated values and categorize
them according to which bin they fall in.  The count for each bin is
accumulated in the dictionary histogram[] indexed according to ind[].
'''
for v in sample:
    for k in ind:
        xk = float(k)
        if xk / bns <= v < (xk + 1) / bns:
            histogram[k] += 1
'''
The following for loop converts each count to a proportion and stores the
proportions in the list h[] and the dictionary histogram[].  The list h[]
will be used for plotting and the dictionary histogram[] for computing.
'''
for k in ind:
    x = histogram[k]
    x = x / float(nsamples)
    h[k] = [x]
    histogram[k] = x
'''
Measuring the degree of convergence of the histogram to the limiting uniform
distribution can be done in various ways.  Here we use the sum of absolute
differences between the expected proportion and the observed proportion.
'''
total = 0.0
for k in ind:
    total = total + abs(expected - histogram[k])
total = format(total, '0.4e')
print('Sum of Absolute Differences= {}'.format(total))

h = array(h)
cell = ind + 0.5  # This will center the bar in the middle of the subinterval.

plt.bar(cell, h, width=0.5, align='center', color='r')
plt.plot(cell, h)

# The following statements are used to form the title for the plot.
# Note how computational information is being included in the title.

string = str(nsamples) + '   Absolute Difference= ' + str(sum)
plt.title('Histogram   n=' + string)

plt.ylabel('Proportions')
plt.xlabel('Subintervals')
plt.show()

# Exercise 1:  Generate a series of plotted histograms using the code.
# Generate five plots using nsample values increasing by powers of ten:  100,
# 1000, 10000, 100000, 1000000.  Calculate the sample mean value and absolute
# difference for each plot.  Evaluate the changes in absoute difference and
# mean values as the sample size increases.  When computing the sample mean
# value, use the dictionary histogram[] and the ind array. The ind array needs
# to be centered within each subinterval. (Hint: add 0.5)

# Exercise 2:  Add code to estimate the sample variance for each random sample.
# For simplicity, calculate the sum of the following terms:
# histogram[k]*(ind[k]-mean)**2.  In the limit as nsamples approaches
# infinity, for sample data grouped in subintervals centered at 0.5, 1.5, ...,
# 8.5, 9.5, the limiting value is 8.25.  Do you see convergence?

src/Classes/MSDS400/Module 9/practice/Module 9 Practice 1.py


import numpy as np

# dot
# For 2-D vectors, it is the equivalent to matrix multiplication.
# For 1-D arrays, it is the inner product of the vectors
# For N-dimensional arrays, it is a sum product over the last axis of a and the second-last axis of b

a = np.array([[1,2],[3,4]])
b = np.array([[11,12],[13,14]])
c = np.dot(a,b) # [[1*11+2*13, 1*12+2*14],[3*11+4*13, 3*12+4*14]]
print("dot prod: ", c)

# vdot
a = np.array([[1,2],[3,4]])
b = np.array([[11,12],[13,14]])
print ("vdot: ", np.vdot(a,b)) # 1*11 + 2*12 + 3*13 + 4*14 = 130

# inner
print ("inner product of 2 1-d vectors: ", inner prodc""np.inner(np.array([1,2,3]),np.array([0,1,0]))
# Equates to 1*0+2*1+3*0

a = np.array([[1,2], [3,4]])
b = np.array([[11, 12], [13, 14]])
print("inner produc: ", np.inner(a,b))
# [[1*11+2*12, 1*13+2*14]
# [3*11+4*12, 3*13+4*14]]

# matmul
a = [[1,0],[0,1]]
b = [[4,1],[2,2]]
print ("Matmul: ", np.matmul(a,b))

a = [[1,0],[0,1]]
b = [1,2]
print ("matmul array n vector: ", np.matmul(a,b))
print ("matmul vector n array: ", np.matmul(b,a))

a = np.arange(8).reshape(2,2,2)
b = np.arange(4).reshape(2,2)
print ("matmul with broadcast: ", np.matmul(a,b))

# TODO: work on determinant, inv and solve methods in numpy


1 EYE IDENTITY
import numpy
print(str(numpy.eye(*map(int,input().split()))).replace('1',' 1').replace('0',' 0'))

2 ARRAYS
import numpy
def arrays(arr):
    # complete this function
    # use numpy.array
    return(numpy.array(arr[::-1], float))

arr = input().strip().split(' ')
result = arrays(arr)
print(result)

3 SHAPE AND RESHAPE
import numpy
a=numpy.array(list(map(int,input().split())))
a.shape=(3,3)
print(a)

4 TRAMPOSE AND FLATTEN
import numpy
N, M = map(int, input().split())
array = numpy.array([input().strip().split() for _ in range(N)], int)
print (array.transpose())
print (array.flatten())

5 CONCATENATE
import numpy

a, b, c = map(int,input().split())
array_1 = numpy.array([input().split() for _ in range(a)],int)
array_2 = numpy.array([input().split() for _ in range(b)],int)
print(numpy.concatenate((array_1, array_2), axis = 0))

6 ZERO AND ONES
import numpy
x= list(map(int,input().split()))
print(numpy.zeros(x,int), numpy.ones(x,int), sep='\n')

7 ARRAY MATH
import numpy
N, M = map(int, input().split())
A, B = (numpy.array([input().split() for _ in range(N)], dtype=int) for _ in range(2))
print(A+B, A-B, A*B, A//B, A%B, A**B, sep='\n')

8 FLOOR, CEIL AND RINT
import numpy
numpy.set_printoptions(sign=' ')
A = numpy.array(input().split(),float)
print(numpy.floor(A))
print(numpy.ceil(A))
print(numpy.rint(A))

9 SUM AND PROD
import numpy
N, M = map(int, input().split())
x = numpy.array([input().split() for _ in range(N)],int)
print(numpy.prod(numpy.sum(x, axis=0), axis=0))

10 MIN AND MAX
import numpy
N, M = map(int, input().split())
print(numpy.array([input().split() for _ in range(int(N))], int).min(1).max())

11 MEAN, VAR AND STD
import numpy
N,M = map(int,input().split())
x = []
for i in range(N):
    y = list(map(int,input().split()))
    x.append(y)

x= numpy.array(x)
numpy.set_printoptions(legacy='1.13')
print (numpy.mean(x, axis = 1))
print (numpy.var(x, axis = 0))
print (numpy.std(x))

12 DOT AND CROSS
import numpy

N = int(input())
A = numpy.array([input().split() for _ in range(N)], int)
B = numpy.array([input().split() for _ in range(N)], int)
print(numpy.dot(A, B))

13 INNER AND OUTER
import numpy
A = numpy.array(input().split(), int)
B = numpy.array(input().split(), int)
print(numpy.inner(A,B), numpy.outer(A,B), sep='\n')

14 POLYNOMIALS
import numpy
P = [float(x) for x in input().split()]
x = float(input())
print(numpy.polyval(P, x))

15 LINEAR ALGEBRA
import numpy
print(round(numpy.linalg.det(numpy.array([list(map(float,input().split())) for _ in range(int(input()))])),2))


import numpy as np

def unit(vector):
    """
    Returns a normalized vector. If zero vector is passed, returns this vector.

    :param vector: Any valid XYZ vector.
    :return: Vector of the same direction as v but of magnitude 1.
    """
    if np.linalg.norm(vector) == 0:
        return vector
    else:
        return vector/np.linalg.norm(vector)


#https://www.geeksforgeeks.org/python-binomial-distribution/
#https://www.hackerrank.com/challenges/s10-binomial-distribution-1/tutorial

import sys
import scipy.stats
import matplotlib.pyplot as plt

def fact(x,memo={}):
    # factorial of x! = x * (x-1) * (x-2) * ..... 2 * 1
    if x in memo:
        return memo[x]
    if x <= 1:
        return 1
    memo[x] = x*fact(x-1,memo)
    return memo[x]

def bin_coe(n,x):
    # binomial coefficient = n! / x!(n-x)!
    return fact(n) / (fact(x) * fact(n-x))

def pmf(n,x,p):
    # probability mass function for the binomial distribution
    return bin_coe(n,x) * (p**x) * ((1-p)**(n-x))

n = 10
x = list(range(1,11))
p = 0.5
q = 0.5

# mean = np; variance = np(1-p)
mean, variance = scipy.stats.binom.stats(n, p)

print(mean, variance)

# p(x) = nCr (p^x) ((1-p)^n-r)
px = [scipy.stats.binom.pmf(r, n, p) for r in x]

print(x)
print(px)

plt.bar(x, px)

plt.title('A coin is tossed 10 times')
plt.xlabel("The value of X")
plt.ylabel("Probability of x")

# plt.hist(px)
plt.show()
plt.savefig('binomial.png')

#Two  lines to make our compiler able to draw:
# plt.savefig(sys.stdout.buffer)
# sys.stdout.flush()

# print(px[4])
# print(sum(px[4:]))
# print(sum(px[:5]))