# Reference: https://en.wikipedia.org/wiki/Activation_function
from functools import partial

import numpy as np

from .utils.generic_utils import get_from_module

class ActivationFunction:
    """Activation function base class"""
    @staticmethod
    def f(x):
        raise NotImplementedError

    @staticmethod
    def df(x):
        raise NotImplementedError

class Linear(ActivationFunction):
    @staticmethod
    def f(x):
        return x

    @staticmethod
    def df(x):
        return 1

class Sigmoid(ActivationFunction):
    """
    Reference: https://en.wikipedia.org/wiki/Sigmoid_function
    """
    @staticmethod
    def f(x):
        return 1 / (1 + np.exp(-x))

    @staticmethod
    def df(x):
        f = Sigmoid.f(x)
        return f * (1 - f)

class Softplus(ActivationFunction):
    """

    """
    @staticmethod
    def f(x):
        return np.log(1 + np.exp(x))

    @staticmethod
    def df(x):
        return Sigmoid.f(x)

class Softsign(ActivationFunction):
    """

    """
    @staticmethod
    def f(x):
        return x / (1 + np.abs(x))

    @staticmethod
    def df(x):
        return 1 / np.square(1 + np.abs(x))

class Tanh(ActivationFunction):
    """
    Reference:
    """
    @staticmethod
    def f(x):
        e_2x = np.exp(-2*x)
        return (1 - e_2x) / (1 + e_2x)

    @staticmethod
    def df(x):
        return 1 - np.square(Tanh.f(x))

class ReLU(ActivationFunction):
    """
    Reference: https://en.wikipedia.org/wiki/Rectifier_(neural_networks)

    """
    @staticmethod
    def f(x):
        return np.where(x > 0, x, 0)

    @staticmethod
    def df(x):
        return np.where(x > 0, 1, 0)

class Softmax(ActivationFunction):
    """
    Reference: https://en.wikipedia.org/wiki/Softmax_function
    """
    @staticmethod
    def f(x):
        e_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
        return e_x / np.sum(e_x, axis=-1, keepdims=True)

    @staticmethod
    def df(x):
        grad = []
        for x_i in x:
            dx_i = np.sum(np.diag(x_i) - x_i * x_i.reshape(-1, 1), axis=-1)
            grad.append(dx_i)
        return np.array(grad)

class ELU(ActivationFunction):
    def __init__(self, alpha=1.0):
        self.alpha = alpha

    def f(self, x):
        return np.where(x < 0, self.alpha * (np.exp(x)-1), x)

    def df(self, x):
        return np.where(x < 0, self.alpha * np.exp(x), 1)

class SELU(ActivationFunction):
    # Reference: Klambauer et al.(2017), https://arxiv.org/abs/1706.02515
    # Code sample: https://github.com/bioinf-jku/SNNs/blob/master/SelfNormalizingNetworks_CNN_MNIST.ipynb
    alpha = 1.6732632423543772848170429916717
    scale = 1.0507009873554804934193349852946

    @staticmethod
    def f(x):
        return np.multiply(SELU.scale, np.where(x < 0, SELU.alpha * (np.exp(x)-1), x))

    @staticmethod
    def df(x):
        return np.multiply(SELU.scale, np.where(x < 0, SELU.alpha * np.exp(x), 1))

ACTIVATIONS = {
    "linear": Linear,
    "tanh": Tanh,
    "sigmoid": Sigmoid,
    "softplus": Softplus,
    "softmax": Softmax,
    "relu": ReLU,
    "elu": ELU
}

get_activation = partial(get_from_module, ACTIVATIONS, "activation")


from diversipy import polytope
import numpy as np
import pytest

def test_constraints_from_bounds():
    A, b = polytope.constraints_from_bounds(lower=[1, 2], upper=[5, 6])
    np.testing.assert_almost_equal(A, [[-1, 0], [0, -1], [1, 0], [0, 1]])
    np.testing.assert_almost_equal(b, [-1, -2, 5, 6])

def test_check_constraints():
    polytope.check_Ab(A=np.ones((2, 3)), b=np.ones(2))  # ok

    with pytest.raises(Exception):
        polytope.check_Ab(A=np.ones((2, 3, 1)), b=np.ones(2))  # A must be 2-dimensional

    with pytest.raises(Exception):
        polytope.check_Ab(A=np.ones((2, 3)), b=np.ones(2, 2))  # b must be 1-dimensional

    with pytest.raises(Exception):
        polytope.check_Ab(
            A=np.ones((4, 3)), b=np.ones(2)
        )  # A and b must have same number of columns

def test_chebyshev_center():
    A, b = polytope.constraints_from_bounds(lower=[0, 0, 0], upper=[1, 1, 1])
    x0 = polytope.chebyshev_center(A, b)
    assert np.allclose(x0, [0.5, 0.5, 0.5])

def test_solve_equality():
    # x1 + x2 + x3 = 2
    A = np.array([[1, 1, 1]])
    b = np.array([2])
    N, xp = polytope.solve_equality(A, b)
    Z = np.random.uniform(low=-1, high=1, size=(5, 2))
    X = Z @ N.T + xp
    assert np.allclose(X @ A.T, b)

def test_hitandrun():
    pass  # tested in test_sample()

def test_sample():
    # Sampling from unit-simplex in R^3
    X = polytope.sample(
        n_points=1000,
        lower=[0, 0, 0],
        upper=[1, 1, 1],
        A2=np.array([[1, 1, 1]]),
        b2=np.array([1]),
    )
    assert np.allclose(X.sum(axis=1), 1)
    assert X.min() >= 0
    assert X.max() <= 1

    # Sampling from [0, 1]^2 subject to
    # x1 / 2 + x2 <= 1
    # 2/3 x1 - x2 <= -0.2
    A1 = np.array([[1 / 2, 1], [2 / 3, -1]])
    b1 = np.array([1, -0.2])
    X = polytope.sample(n_points=1000, lower=[0, 0], upper=[1, 1], A1=A1, b1=b1)
    assert np.all(X @ A1.T <= b1)


# coding: utf-8

# # Introduction to Theano

# For a Theano tutorial please see: http://deeplearning.net/software/theano/tutorial/index.html

# ## Basic Operations

# For more details see: http://deeplearning.net/software/theano/tutorial/adding.html
#
# **Task**:  Use Theano to compute a simple polynomial function $$f(x,y) = 3x+xy+3y$$
#
# Hints:
# - First define two input variables with the correct type (http://deeplearning.net/software/theano/library/tensor/basic.html#all-fully-typed-constructors)
# - Define the computation of the function and store it in a variable
# - Use the theano.function() to compile your computation graph
#

# In[8]:

import theano
import theano.tensor as T

x = T.dscalar('x') #First input variable to the compute graph
y = T.dscalar('y') #Second input variable to the compute graph
z = 3*x + x*y + 3*y #Our formula we like to compute

#Compile for the output z, given the inputs x and y
f = theano.function(inputs=[x,y], outputs=z)

# Now you can invoke f and pass the input values, i.e. f(1,1), f(10,-3) and the result for this operation is returned.

# In[9]:

print f(1,1)
print f(10,-3)

# **Printing of the graph**
#
# You can print the graph for the above value of z. For details see:
# http://deeplearning.net/software/theano/library/printing.html
# http://deeplearning.net/software/theano/tutorial/printing_drawing.html
#
# **To print the graph, futher libraries must be installed. In 99% of your development time you don't need the graph printing function. Feel free to skip this section**

# In[10]:

#Graph for z
theano.printing.pydotprint(z, outfile="pics/z_graph.png", var_with_name_simple=True)

#Graph for function f (after optimization)
theano.printing.pydotprint(f, outfile="pics/f_graph.png", var_with_name_simple=True)

# **The graph fo z:**
#
#
# **The graph for f:**
#

# ## Simple matrix multiplications

# The following types for input variables are typically used:
#
#     byte: bscalar, bvector, bmatrix, btensor3, btensor4
#     16-bit integers: wscalar, wvector, wmatrix, wtensor3, wtensor4
#     32-bit integers: iscalar, ivector, imatrix, itensor3, itensor4
#     64-bit integers: lscalar, lvector, lmatrix, ltensor3, ltensor4
#     float: fscalar, fvector, fmatrix, ftensor3, ftensor4
#     double: dscalar, dvector, dmatrix, dtensor3, dtensor4
#     complex: cscalar, cvector, cmatrix, ctensor3, ctensor4
#
# scalar: One element (one number)
# vector: 1-dimension
# matrix: 2-dimensions
# tensor3: 3-dimensions
# tensor4: 4-dimensions
#
# As we do not need perfect precision we use mainly float instead of double. Most GPUs are also not able to handle doubles.
#
# So in practice you need: iscalar, ivector, imatrix and fscalar, fvector, vmatrix.
#
# **Task**: Implement the function $$f(x,W,b) = \tanh(xW+b)$$ with $x \in \mathbb{R}^n, b \in \mathbb{R}^k, W \in \mathbb{R}^{n \times k}$.
#
# $n$ input dimension and $k$ output dimension

# In[11]:

import theano
import theano.tensor as T
import numpy as np

x = T.fvector('x')
W = T.fmatrix('W')
b = T.fvector('b')

activation = T.dot(x,W)+b
z = T.tanh(activation)

f = theano.function(inputs=[x,W,b], outputs=[activation,z])

# Next we define some NumPy-Array with data and let Theano compute the result for $f(x,W,b)$

# In[12]:

inputX = np.asarray([0.1, 0.2, 0.3], dtype='float32')
inputW = np.asarray([[0.1,-0.2],[-0.4,0.5],[0.6,-0.7]], dtype='float32')
inputB = np.asarray([0.1,0.2], dtype='float32')

print "inputX.shape",inputX.shape
print "inputW.shape",inputW.shape

f(inputX, inputW, inputB)

# Don't confuse x,W, b with inputX, inputW, inputB. x,W,b contain pointer to your symbols in the compute graph. inputX,inputW,inputB contains your data.

# ## Shared Variables and Updates
# See: http://deeplearning.net/software/theano/tutorial/examples.html#using-shared-variables
#
# - Using shared variables, we can create an internal state.
# - Creation of a accumulator:
#     - At the beginning initialize the state to 0
#     - With each function call update the state by certain value
# - Later, in your neural networks, the weight matrices $W$ and the bias values $b$ will be stored as internal state / as shared variable.
# - Shared variables improve performance, as you need less transfer between your Python code and the execution of the compute graph (which is written & compiled from C code)
# - Shared variables can also be store on your graphic card

# In[13]:

import theano
import theano.tensor as T
import numpy as np

#Define my internal state
init_value = 1

state = theano.shared(value=init_value, name='state')

#Define my operation f(x) = 2*x
x = T.lscalar('x')
z = 2*x

accumulator = theano.function(inputs=[], outputs=z, givens={x: state})

print accumulator()
print accumulator()

# ### Shared Variables
# - We use theano.shared() to share a variable (i.e. make it internally available for Theano)
# - Internal state variables are passed by compile time via the parameter *givens*. So to compute the ouput *z*, use the shared variable *state* for the input variable *x*
# - For information on the borrow=True parameter see: http://deeplearning.net/software/theano/tutorial/aliasing.html
# - In most cases we can set it to true and increase by this the performance.
#
#

# ## Updating Shared Variables
# - Using the *updates*-parameter, we can specify how our shared variables should be updated
# - This is useful to create a train function for a neural network.
#     - We create a function *train(data)* which computes the error and gradient
#     - The computed gradient is then used in the same call to update the shared weights
#     - Training just becomes: *for mini_batch in mini_batches: train(mini_batch)*

# In[14]:

#New accumulator function, now with an update
inc = T.lscalar('inc')
accumulator = theano.function(inputs=[inc], outputs=(state,z), givens={x: state}, updates=[(state,state+inc)])

print accumulator(1)
print accumulator(1)
print accumulator(1)

# - In the above example we increase the state by the variable *inc*
# - The value for *inc* is passed as value to our function

# In[ ]:


'''
Closeness centrality at a node is 1/average distance to all other nodes.

The closeness centrality is normalized to to n-1 / size(G)-1 where n is the number of nodes in the connected part of graph containing the node. If the graph is not completely connected, this algorithm computes the closeness centrality for each connected part separately.

It measures how fast information spreads from a given node to other reachable nodes in the graphs. For a node $u$, it represents the reciprocal of the average shortest path length between $u$ and every other reachable node in the graph:

'''
import networkx as nx

def calculate(network):
    try:
        n = nx.closeness_centrality(network)
    except:
        return 0

    if len(n.values()) == 0:
        return 0
    else:
        return round(sum(n.values())/len(n.values()), 7)


#python3 steven
#plot logistic map equation
#https://en.wikipedia.org/wiki/Logistic_map
#logistic map equation:  x := r*x*(1-x)

import numpy as np
import matplotlib.pyplot as plt

# Logistic function implementation
def logistic_eq(r,x):
    #return r*x*(1-x)
    #return r*x*(1-x)**2
    return r*x*(1-x)**3
    #return r*x*(1-x)**5

# Iterate the function for a given r
def logistic_equation_orbit(seed, r, n_iter, n_skip=0):
    print('Orbit for seed {0}, growth rate of {1}, plotting {2} iterations after skipping {3}'.format(seed, r, n_iter, n_skip))
    X=[]
    T=[]
    t=0
    x = seed
    # Iterate the logistic equation, printing only if n_skip steps have been skipped
    for i in range(n_iter + n_skip):
        if i >= n_skip:
            X.append(x)
            T.append(t)
            t+=1
        x = logistic_eq(r,x)

    return T,X

def bifurcation_diagram(seed, n_skip, n_iter, step=0.0001, r_min=0,r_max=4):
    print("Starting with x0 seed {0}, skip plotting first {1} iterations, then plot next {2} iterations.".format(seed, n_skip, n_iter))

    R = []
    X = []
    r_range = np.linspace(r_min, r_max, int(1/step))

    for r in r_range:
        x = seed
        # For each r, iterate the logistic function and collect datapoint if n_skip iterations have occurred
        for i in range(n_iter+n_skip+1):
            if i >= n_skip:
                R.append(r)
                X.append(x)

            x = logistic_eq(r,x)

    return R,X

def plot_equation_orbit(T,X):
    ax = plt.subplot(1,1,1)
    plot_equation_orbitAx(T,X,ax)
    plt.show()

def plot_equation_orbitAx(T,X,ax):
    ax.plot(T, X)
    ax.set_ylim(0, 1)
    ax.set_xlim(0, T[-1])
    ax.set_xlabel('Time t')
    ax.set_ylabel('X')

def plotBifurcation(R,X,r_min=0, r_max=4.0):
    ax = plt.subplot(1,1,1)
    plotBifurcationAx(R,X,r_min,r_max,ax)
    plt.show()

def plotBifurcationAx(R,X,r_min=0, r_max=4.0,ax=None):
    ax.plot(R, X, ls='', marker=',')
    ax.set_ylim(0, 1)
    ax.set_xlim(r_min, r_max)
    ax.set_xlabel('r')
    ax.set_ylabel('X')

def singleBifurcation():
    rMin = 0
    rMax = 9
    R,X = bifurcation_diagram(0.2, 5, 10, r_min=rMin,r_max=rMax)
    ax = plt.subplot(1,1, 1)
    ax.set_title('LogisticMap:r*x*(1-x)**3')
    plotBifurcationAx(R,X,rMin,rMax,ax)
    plt.show()

def multBifurcation():
    rMin = 0
    rMax = 9

    nSkips=[1,2,5,10,20,50]
    plt.suptitle("LogisticMap:r*x*(1-x)**3")
    for i,nSkips in enumerate(nSkips):
        R,X = bifurcation_diagram(0.2, nSkips, 5, r_min=rMin,r_max=rMax)

        ax = plt.subplot(3, 2, i+1)
        ax.set_title('nSkips='+str(nSkips))
        plotBifurcationAx(R,X,rMin,rMax,ax)
    plt.show()

    '''
    nIters=[1,5,10,20,50,100]
    plt.suptitle("LogisticMap:r*x*(1-x)**3")
    for i,nIter in enumerate(nIters):
        R,X = bifurcation_diagram(0.2, 1, nIter, r_min=rMin,r_max=rMax)

        ax = plt.subplot(3, 2, i+1)
        ax.set_title('nIter='+str(nIter))
        plotBifurcationAx(R,X,rMin,rMax,ax)
    plt.show()
    '''

def main():
    #x:= r*x*(1-x)
    #logistic_equation_orbit(0.1, 3.05, 100)
    #logistic_equation_orbit(0.1, 3.9, 100)
    #T,X = logistic_equation_orbit(0.2, 7, 100, 1000)
    #plot_equation_orbit(T,X)
    #return

    '''
    rl = [6, 6.5, 7, 7.5, 8.5, 9]
    for i,r in enumerate(rl):
        T,X = logistic_equation_orbit(0.2, r, 100, 1000)

        ax = plt.subplot(3, 2, i+1)
        ax.set_title('r='+str(r))
        plot_equation_orbitAx(T,X,ax)
    plt.show()
    return
    '''

    #bifurcation_diagram(0.2, 100, 5)
    #bifurcation_diagram(0.2, 100, 10)
    #bifurcation_diagram(0.2, 100, 10, r_min=2.7)
    #rMin = 0
    #R,X = bifurcation_diagram(0.2, 100, 10, r_min=rMin)
    #plotBifurcation(R,X,r_min=rMin)

    #x:= r*x*(1-x)**2

    multBifurcation()

if __name__=='__main__':
    main()


# exercise 5.2.3

from matplotlib.pyplot import figure, plot, xlabel, ylabel, legend, show, ylim
import numpy as np
import sklearn.linear_model as lm

# Parameters
Kd = 5  # no of terms for data generator
Km = 3  # no of terms for regression model
N = 50  # no of data objects to train a model
Xe =  np.linspace(-2,2,1000).reshape(-1,1) # X values to visualize true data and model
eps_mean, eps_std = 0, 0.5          # noise parameters

# Generate dataset (with noise)
X = np.linspace(-2,2,N).reshape(-1,1)
Xd = np.power(X, range(1,Kd+1))
eps = (eps_std*np.random.randn(N) + eps_mean)
w = -np.power(-.9, range(1,Kd+2))
y = w[0] + Xd @ w[1:] + eps

# True data generator (assuming no noise)
Xde = np.power(Xe, range(1,Kd+1))
y_true = w[0] + Xde @ w[1:]

# Fit ordinary least squares regression model
Xm = np.power(X, range(1,Km+1))
model = lm.LinearRegression()
model = model.fit(Xm,y)

# Predict values
Xme = np.power(Xe, range(1,Km+1))
y_est = model.predict(Xme)

# Plot original data and the model output
f = figure()
plot(X,y,'.')
plot(Xe,y_true,'-')
plot(Xe,y_est,'-')
xlabel('X'); ylabel('y'); ylim(-2,8)
legend(['Training data', 'Data generator K={0}'.format(Kd), 'Regression fit (model) K={0}'.format(Km)])

show()

print('Ran Exercise 5.2.3')


import numpy as np
import sys
if "../" not in sys.path:
    sys.path.append("../")
from lib.envs.gridworld import GridworldEnv

env = GridworldEnv()

def policy_eval(policy, env, v, df=1.0, theta=0.00001):
    v_new = np.zeros(env.nS)
    for i in range(env.nS):
        for j in range(env.nA):
            for prob, next_state, reward, done in env.P[i][j]:
                v_new[i] += (policy[i][j] * (reward + (df * prob * v[next_state])))
    for i in range(env.nS):
        v[i] = v_new[i]
    return np.array(v)

def policy_improvement(policy, env, vf, df=1.0):
    for i in range(env.nS):
        max_val = -1 * float("inf")
        best_a = -1
        for j in range(env.nA):
            for prob, next_state, reward, done in env.P[i][j]:
                val = reward + (df * prob * vf[next_state])
                if max_val < val:
                    max_val = val
                    best_a = j
        for j in range(env.nA):
            policy[i] = np.eye(env.nA)[best_a]
    return np.array(policy)

#initiate with uniform policy and zeroed value function
p = np.ones([env.nS, env.nA]) / env.nA
v = np.zeros(env.nS)

#policy convergence flag
policy_stable = False

#policy iteration loop
while (not policy_stable):
    p_backup = np.copy(p)
    v = policy_eval(p, env, v)
    p = policy_improvement(p, env, v)
    for i in range(env.nS):
        if np.argmax(p_backup[i]) != np.argmax(p[i]):
            break
    if i == (env.nS - 1):
        policy_stable = True

# Test: Make sure the evaluated policy is what we expected
expected_v = np.array([ 0, -1, -2, -3, -1, -2, -3, -2, -2, -3, -2, -1, -3, -2, -1,  0])
print "expected_v: ", expected_v
print "evaluated_v: ", v
np.testing.assert_array_almost_equal(v, expected_v, decimal=2)


from utils import r
from sympy import lambdify, latex

def rk4(f, h, x0, y0, iterations=2, i=1):
    out_str = f"\\textbf{{Iteration {i}}}\n"
    out_str += f'($x_{i-1}$, $y_{i-1}$) = ({r(x0)}, {r(y0)})\n\n'

    out_str += f'$$ y_{i} = y_{i-1} + \\frac{{1}}{{6}}\\left[k_1+2k_2+2k_3+k_4\\right]h $$\n'
    out_str += 'Where,\n'
    out_str += f'$$ k_1 = f(x_{i-1}, y_{i-1}) $$\n'
    out_str += f'$$ k_2 = f(x_{i-1}+\\frac{{1}}{{2}}h, y_{i-1}+\\frac{{1}}{{2}}k_1h) $$\n'
    out_str += f'$$ k_3 = f(x_{i-1}+\\frac{{1}}{{2}}h, y_{i-1}+\\frac{{1}}{{2}}k_2h) $$\n'
    out_str += f'$$ k_4 = f(x_{i-1}+h, y_{i-1}+k_3h) $$\n\n'

    x, y = symbols('x y')
    fn = lambdify((x, y), f)
    k1 = fn(x0, y0)
    k2 = fn(x0+1/2*h, y0+1/2*k1*h)
    k3 = fn(x0+1/2*h, y0+1/2*k2*h)
    k4 = fn(x0+h, y0+k3*h)
    y2 = y0 + 1/6*(k1+2*k2+2*k3+k4)*h

    out_str += f'$$ k_1 = f({r(x0)}, {r(y0)}) = {latex(f)} = {r(k1)} $$\n'
    out_str += f'$$ k_2 = f\\left({r(x0)}+\\frac{{1}}{{2}}({h}), {r(y0)}+\\frac{{1}}{{2}}({r(k1)})({h})\\right) = {latex(f)} = {r(k2)} $$\n'
    out_str += f'$$ k_3 = f\\left({r(x0)}+\\frac{{1}}{{2}}({h}), {r(y0)}+\\frac{{1}}{{2}}({r(k2)})({h})\\right) = {latex(f)} = {r(k3)} $$\n'
    out_str += f'$$ k_4 = f\\left({r(x0)}+({h}), {r(y0)}+({r(k3)})({h})\\right) = {latex(f)} = {r(k4)} $$\n'

    out_str += f'\n Substituting values\n'
    out_str += f'$$ y_{i} = {r(y0)} + \\frac{{1}}{{6}}\\left[{r(k1)}+2({r(k2)})+2({r(k3)})+{r(k4)}\\right]({r(h)}) = {r(y2)} $$\n'

    out_str += f'\n$$ (x_{i}, y_{i}) = ({r(h+x0)}, {r(y2)}) $$\n'

    if iterations > 1:
        out_str += rk4(f, h, h+x0, y2, iterations-1, i+1)

    return out_str

if __name__ == '__main__':
    from sympy import symbols

    x, y = symbols('x y')
    print(rk4((x+0-y)/2, 0.2, 0, 1, 2))


'''
Logarithm Curve
@ author: Suyash Shivaji Phatak
Date = 25\04\2020
'''

# Defining Modules
import numpy as np
import matplotlib.pyplot as plt

# Taking input from user
range_for_log = int(input('\nEnter a range of numbers for logarithm with base 2,10 & e: '))

# Log with base 2

# Setting x
x = np.arange(1, range_for_log)

# Setting y fo base 2
y1 = np.log2(x)

# Setting y for base 10
y2 = np.log10(x)

# Setting y for base e
y3 = np.log(x)

# Plotting Points
plt.plot(x,y1, label='Log With Base 2')
plt.plot(x,y2, label='Log With Base 10')
plt.plot(x,y3, label='Log With Base e')
# Seperating figure
plt.figure(1)

# Naming the x axis
plt.xlabel('X')

# Naming the y axis
plt.ylabel('Y')

# Giving a title to graph
plt.title('Logarithm Function')

# Showing legend
plt.legend()
# Function to show the plot
plt.show()


import numpy as np

def camel_hump_six_old(x1, x2):
    x1_2 = np.square(x1)
    x1_4 = np.square(x1_2)
    x2_2 = np.square(x2)
    term1 = (4.0 - 2.1 * x1_2 + x1_4 / 3.0) * x1_2
    # term1 = term1*x1_2
    prod = x1 * x2
    term2 = (4.0 * x2_2 - 4) * x2_2
    # term2 = term2*x2_2
    fx = term1 + prod + term2
    return fx

def camel_hump_six(xx):
    """
    minima: -1.03316 for
    Range: [-3, 3, -2, 2]
    :param xx: minimum x = (0.0898, -0.7126), (-0.0898, 0.7126)
    :return: function value
    """
    x1_2 = np.square(xx[0])
    x1_4 = np.square(x1_2)
    x2_2 = np.square(xx[1])
    term1 = (4.0 - 2.1 * x1_2 + x1_4 / 3.0) * x1_2
    # term1 = term1*x1_2
    prod = xx[0] * xx[1]
    term2 = (4.0 * x2_2 - 4) * x2_2
    # term2 = term2*x2_2
    fx = term1 + prod + term2
    return fx

def f(x, y):
    return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)

def rastringin(x1, x2, n=2, A=0.5):

    def calc(x):
        return np.square(x) - A * np.cos(2 * np.pi * x)

    fx = A * n + calc(x1) + calc(x2)
    return fx

def rastringin_gen(xx, n=2, A=10):
    """
    optimum is at 0.0 when all xx = 0
    range:  [-5.11, 5.11, -5.11, 5.11, ...]
    :param xx: array of input variables
    :param n:  number of input variables
    :param A:
    :return: function value
    """
    def calc(xv):
        return np.square(xv) - A * np.cos(2 * np.pi * xv)

    summation = 0
    for x in xx:
        summation += calc(x)
    fx = A * n + summation
    return fx


'''
Linear Regression by SVD on TensorFlow
Author: Rowel Atienza
Project: https://github.com/roatienza/Deep-Learning-Experiments
'''
# On command line: python3 linear_inv.py
# Prerequisite: tensorflow 1.0 (see tensorflow.org)
#             : matplotlib (http://matplotlib.org/)

from __future__ import print_function

import tensorflow as tf
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np

# Tunable parameters (Try changing the values and see what happens)
# Variable samples >= 3 ; stddev > 0.; xcoeff are real numbers
samples = 100
stddev = 1.0
# xcoeff should be predicted by solving y = A*x using SVD; try changing the values
xcoeff = tf.transpose(tf.constant([[2., -3.5, 12.5]]))

# The computation
# We get elements of A by sampling a normal distribution
a = tf.Variable(tf.random_normal([1, samples],stddev=stddev))
# Sort to produce a nice plot later
b, _ = tf.nn.top_k(a,k=samples)
# Correct the shape
a = tf.reshape(b,[samples,1])

# Inputs to form y = a*a*xcoeff[0] + a*xcoeff[1] + xcoeff[2]
A = tf.concat([tf.concat([a*a,a],1),tf.ones_like(a)],1)
# Observable outputs
y = tf.matmul(A,xcoeff)

# SVD - Singular Value Decomposition
d, U, V = tf.svd(A, full_matrices=True, compute_uv=True)

# Wondering why tensorflow does not generate the diagonal matrix directly
# D is the diagonal matrix with the diagonal elements being the reciprocal of d
D = tf.diag(np.reciprocal(d))

# D is actually nxm so zero padding is needed
r = D.get_shape().as_list()[0]
Z = tf.zeros([r,samples-r])
D = tf.concat([D,Z],1)

# This is linear regression by SVD
# Ax = y  mxn nx1 = mx3 3x1 = mx1
# x = Inv(A)y = nxm mx1 = 3xm mx1  ; x is our predicted xcoeff
# Inv(A) = A_ = VDtran(U) = nxn nxm mxm = nxm = 3x3 3xm 3xm = 3xm

# x (predicted xcoeff) is determined using Moore-Penrose pseudoinverse
A_ = tf.matmul(V,tf.matmul(D,tf.transpose(U)))
x = tf.matmul(A_,y)

init = tf.global_variables_initializer()
with tf.Session() as session:
    session.run(init)
    # values are the xcoeff
    print("Actual x =")
    print(xcoeff.eval())

    print("\nPredicted x = ")
    print(x.eval())

    # Let's plot; Make all variables 1D by reshaping
    a = tf.reshape(a,[a.get_shape().as_list()[0]])
    y = tf.reshape(y,[y.get_shape().as_list()[0]])
    # Predicted model, yp, based on x
    yp = tf.matmul(A,x)
    yp = tf.reshape(yp,[yp.get_shape().as_list()[0]])
    plt.plot(a.eval(), y.eval(), 'ro', a.eval(), yp.eval(), 'b')
    red = mpatches.Patch(color='red', label='Data')
    blue = mpatches.Patch(color='blue', label='Model')
    plt.legend(handles=[red,blue])
    plt.show()


import numpy as np
from scipy.stats import entropy
from math import log, e
import pandas as pd

import timeit

def entropy1(labels, base=None):
  value,counts = np.unique(labels, return_counts=True)
  return entropy(counts, base=base)

def entropy2(labels, base=None):
  """ Computes entropy of label distribution. """

  n_labels = len(labels)

  if n_labels <= 1:
    return 0

  value,counts = np.unique(labels, return_counts=True)
  probs = counts / n_labels
  n_classes = np.count_nonzero(probs)

  if n_classes <= 1:
    return 0

  ent = 0.

  # Compute entropy
  base = e if base is None else base
  for i in probs:
    ent -= i * log(i, base)

  return ent

def entropy3(labels, base=None):
  vc = pd.Series(labels).value_counts(normalize=True, sort=False)
  base = e if base is None else base
  return -(vc * np.log(vc)/np.log(base)).sum()

def entropy4(labels, base=None):
  value,counts = np.unique(labels, return_counts=True)
  norm_counts = counts / counts.sum()
  base = e if base is None else base
  return -(norm_counts * np.log(norm_counts)/np.log(base)).sum()

labels = [1,3,5,2,3,5,3,2,1,3,4,5]

print(entropy1(labels))
print(entropy2(labels))
print(entropy3(labels))
print(entropy4(labels))


import numpy

def cg(x_mat, y_vec, tol=1e-12, max_iter_int=10000):
    '''
    Conjugate Gradient (CG) Algorithm for Least Squares Regression: argmin_w || X * w - y ||_2^2

    Input
        x_mat: n-by-d NumPy matrix X;
        y_vec: n-dim vector y;
        tol: convergence tolerance (optional);
        max_iter_int: (>0) maximum number of iterations.

    Output
        w_vec: solution to the LSR problem;
        is_converged_bool: whether CG attains convergence tolerance.
    '''

    n_int, d_int = x_mat.shape
    y_vec = numpy.dot(x_mat.T, y_vec.reshape(n_int, 1))

    w_vec = numpy.zeros((d_int, 1))

    xw_vec = numpy.dot(x_mat, w_vec)
    r_vec = y_vec - numpy.dot(x_mat.T, xw_vec)
    p_vec = r_vec
    rsold_real = numpy.sum(r_vec ** 2)

    is_converged_bool = False
    i = -1

    for i in range(max_iter_int):
        xp_vec = numpy.dot(x_mat.T, numpy.dot(x_mat, p_vec))
        alp_real = rsold_real / numpy.sum(p_vec * xp_vec)
        w_vec += alp_real * p_vec
        r_vec -= alp_real * xp_vec
        rsnew_real = numpy.sum(r_vec ** 2)

        if i % 100 == 0:
            print('Iteration ' + str(i) + ': residual=' + str(rsnew_real))

        if rsnew_real < tol:
            is_converged_bool = True
            print('Iteration ' + str(i) + ': residual=' + str(rsnew_real))
            break

        p_vec = r_vec + (rsnew_real / rsold_real) * p_vec
        rsold_real = rsnew_real

    if not is_converged_bool:
        print('Warn: CG did not converge after ' + str(i+1) + ' iterations!')

    return w_vec, is_converged_bool

def demo_cg(x_mat, y_vec, w_opt_vec, tol=1e-12, max_iter_int=10000):
    '''
    For Demo Usage Only!

    Conjugate Gradient (CG) Algorithm for Least Squares Regression: argmin_w || X * w - y ||_2^2

    Input
        x_mat: n-by-d NumPy matrix X;
        y_vec: n-dim vector y;
        w_opt_vec: optimal solution;
        tol: convergence tolerance (optional);
        max_iter_int: maximum number of iterations.

    Output
        w_vec: solution to the LSR problem;
        is_converged_bool: whether CG attains convergence tolerance;
        tmp_err_vec: the error || X * w_opt - X * w ||_2^2 of each step.
    '''

    tmp_xw_opt_vec = numpy.dot(x_mat, w_opt_vec.reshape(len(w_opt_vec), 1)) # for test only

    n_int, d_int = x_mat.shape
    y_vec = numpy.dot(x_mat.T, y_vec.reshape(n_int, 1))

    w_vec = numpy.zeros((d_int, 1))

    xw_vec = numpy.dot(x_mat, w_vec)
    r_vec = y_vec - numpy.dot(x_mat.T, xw_vec)
    p_vec = r_vec
    rsold_real = numpy.sum(r_vec ** 2)

    is_converged_bool = False

    tmp_err_vec = numpy.zeros(max_iter_int) # for test only

    for i in range(max_iter_int):
        xp_vec = numpy.dot(x_mat.T, numpy.dot(x_mat, p_vec))
        alp_real = rsold_real / numpy.sum(p_vec * xp_vec)
        w_vec += alp_real * p_vec
        r_vec -= alp_real * xp_vec
        rsnew_real = numpy.sum(r_vec ** 2)

        if i % 100 == 0:
            print('Iteration ' + str(i) + ': residual=' + str(rsnew_real))

        tmp_dist_vec = numpy.dot(x_mat, w_vec) - tmp_xw_opt_vec # for test only
        tmp_err_vec[i] = numpy.sum(tmp_dist_vec ** 2) # for test only

        if rsnew_real < tol:
            is_converged_bool = True
            print('Iteration ' + str(i) + ': residual=' + str(rsnew_real))
            break

        p_vec = r_vec + (rsnew_real / rsold_real) * p_vec
        rsold_real = rsnew_real

    if not is_converged_bool:
        print('Warn: CG did not converge after ' + str(i+1) + ' iterations!')

    tmp_err_vec = tmp_err_vec[0: i+1] # for test only

    return w_vec, is_converged_bool, tmp_err_vec


import numpy as np
import matplotlib.pyplot as plt

class Sine_Wave:
    def __init__(self,A,f,t,res,phase):
        self.A = A
        self.f= f
        self.t= np.arange(0,t,res)
        self.phase= phase
        self.phi= phase*np.pi
        self.calc_x()
    def calc_x(self):
        self.x = self.A*np.sin(2*np.pi*self.f*self.t + self.phi)
    def show_plot(self,x0=0,x1=8,y0=-5,y1=5):   #0,8,-5,5
        #self.calc_x()
        plt.plot(self.t,self.x)
        plt.axis([x0,x1,y0,y1])
        plt.xlabel('time in seconds')
        plt.ylabel('amplitude')
        plt.show()

if  __name__ == "__main__":
    s1=Sine_Wave(3,.25,8,0.01,0)
    s1.show_plot()


import math
import numpy as np
import scipy.ndimage.filters as fi

def mse(noisy: np.ndarray, clean: np.ndarray) -> float:
    """
    Calculate the MSE value between noise and clean image.

    :param noisy: ndarray, noisy image
    :param clean: ndarray, clean image
    :return: float, mean-square-error value
    """
    assert noisy.shape == clean.shape, "Shape mismatch when computing MSE."
    res = np.square(noisy.astype(float) - clean.astype(float)).mean()
    return res

def psnr(noisy: np.ndarray, clean: np.ndarray, dynamic: float=1.0) -> float:
    """
    Calculate the PSNR value between noise and clean image.

    :param noisy: ndarray, noisy image
    :param clean: ndarray, clean image
    :param dynamic: float, the scale (max value) of the image
    :return: float, PSNR value
    """
    assert noisy.shape == clean.shape, "Shape mismatch when computing PSNR."
    peak = dynamic * dynamic
    return 10 * math.log10(peak / mse(noisy, clean))

def gaussian_kernel(size: int, sigma: float=1, scale: float=1) -> np.ndarray:
    """
    Create a 2-dimensional Gaussian kernel

    :param size: integer, size of the kernel / width and height
    :param sigma: float, standard variance of the kernel
    :param scale: float, sum of the output
    :return: ndarray, Gaussian kernel array
    """
    inp = np.zeros((size, size))
    # set element at the middle to one, a dirac delta
    inp[size // 2, size // 2] = 1
    # gaussian-smooth the dirac, resulting in a gaussian filter mask
    return fi.gaussian_filter(inp, sigma) * scale


import sys
import numpy as np
import matplotlib.pyplot as plt
import pandas

def sigmoid(z):
    return 1./(1.+np.exp(-z))

def predict(X, weights):
  '''
  Returns 1D array of probabilities
  that the class label == 1
  '''
  z = np.dot(X, weights)
  return sigmoid(z)

def decision_boundary(prob):
  return 1 if prob >= .5 else 0

def classify(preds):
  f = np.vectorize(decision_boundary)
  return f(preds).flatten()

def cost_function(X, y, weights):
    '''
    Using Mean Absolute Error
    Features:(100,3)
    Labels: (100,1)
    Weights:(3,1)
    Returns 1D matrix of predictions
    Cost = ( log(predictions) + (1-labels)*log(1-predictions) ) / len(labels)
    '''
    observations = len(y)
    predictions = predict(X, weights)
    #Take the error when label=1
    class1_cost = -y*np.log(predictions)
    #Take the error when label=0
    class2_cost = (1-y)*np.log(1-predictions)
    #Take the sum of both costs
    cost = class1_cost - class2_cost
    #Take the average cost
    cost = cost.sum()/observations
    return cost

def calc_delta(X, y, weights, lr):
    '''
    Vectorized Gradient Descent
    Features:(200, 3)
    Labels: (200, 1)
    Weights:(3, 1)
    '''
    n = X.shape[0]
    #1 - Get Predictions
    predictions = predict(X, weights)
    #2 Transpose features from (200, 3) to (3, 200)
    # So we can multiply w the (200,1)  cost matrix.
    # Returns a (3,1) matrix holding 3 partial derivatives --
    # one for each feature -- representing the aggregate
    # slope of the cost function across all observations
    gradient = np.dot(X.T, predictions - y)
    #3 Take the average cost derivative for each feature
    gradient /= n
    #4 - Multiply the gradient by our learning rate
    gradient *= lr
    return gradient

def train(X, y, weights, lr, iters):
    cost_history = []
    delta_history = []
    weight_history = []
    for i in range(iters):
        gradient = calc_delta(X, y, weights, lr)
        weights -= gradient
        #Calculate error for auditing purposes
        cost = cost_function(X, y, weights)
        cost_history.append(cost)
        delta_history.append(gradient.flatten())
        weight_history.append(weights.flatten())
        # Log Progress
        if i % 1000 == 0:
            print("iter: "+str(i) + " cost: "+str(cost))
    return weights, cost_history, delta_history, weight_history

def accuracy(predicted_labels, actual_labels):
    diff = predicted_labels - actual_labels
    return 1.0 - (float(np.count_nonzero(diff)) / len(diff))

def plot_decision_boundary(trues, falses):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter([i for i in range(len(trues))], trues, s=25, c='b', marker="o", label='Trues')
    ax.scatter([i for i in range(len(falses))], falses, s=25, c='r', marker="s", label='Falses')
    plt.legend(loc='upper right');
    ax.set_title("Decision Boundary")
    ax.set_xlabel('N/2')
    ax.set_ylabel('Predicted Probability')
    plt.axhline(.5, color='black')
    plt.show()

def normalize(v):
    upper=v.max(0)
    lower=v.min(0)
    v=2*(v-lower)/(upper-lower) -1
    return v;

def load_data(fn, do_normalize=False):
    d=pandas.read_csv(fn, header=None)
    (n,m)=d.shape
    y=d[m-1].values
    y.resize(n,1)
    v=d.iloc[:,0:m-1].values
    if do_normalize:
        v=normalize(v)
    X=np.hstack((v, np.ones([n,1])))
    return (X,y)

'''
return a matrix (w*n), each column is a weight vector
'''
def load_param(fn):
    d=pandas.read_csv(fn, header=None)
    (n,m)=d.shape
    w=d.iloc[:,2:].values
    return w.transpose()

def dump_param(fn, weight):
    f=open(fn, 'w')
    s=','.join([str(v) for v in weight.flatten()])
    f.write(s)
    f.close()

def main(fn, lrate, iters):
    X,y=load_data(fn)
    n,m=X.shape
    weight=np.zeros([m, 1])+0.01
    weight, chis, whis, ghis= train(X, y, weight, lrate, iters)
    prob=predict(X, weight)
    pred=classify(prob)
    acc = accuracy(pred, y)
    print('accuracy: '+str(acc))
    if m-1==2:
        trues=X[pred==y,0:2]
        falses=X[pred!=y,0:2]
        plot_decision_boundary(trues, falses)

if __name__ == '__main__':
    if len(sys.argv) <= 3:
        print('Usage: <fn> <lrate> <iter>')
        #exit(0)
    else:
        fn = sys.argv[1]
        lrate = float(sys.argv[2])
        iters = int(sys.argv[3])
        #main(fn, lrate, iters)


# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
import scipy.linalg as la
from sklearn.decomposition import PCA

'''
Data
'''
data = np.matrix([[1,2,4],
               [4,1,2],
               [5,4,8]])

df = pd.DataFrame(data)

'''
Manual calculation
'''

# standardize data
standardized_data  = (df - df.mean()) / (df.std())

# Finding covariance
covarance = np.cov(standardized_data.T, bias = 1)

# find eigen value& eigen vector
eigenvalue, eigenvectors = np.linalg.eig(covarance)

# Find PCA
n_components = 3

pca_manual = np.matmul(np.array(standardized_data),eigenvectors)

pca_manual  = pca_manual[:,:n_components]

'''
calculate using SKlearn
'''

# PCA
pca_sklearn = (PCA(n_components).fit_transform(standardized_data))

print('Standardized data')
print(standardized_data.round(2))
print('')

print('Covariance')
print(covarance.round(2))
print('')

print('eigen_value')
print(eigenvalue.round(4))
print('')

print('eigen_vector')
print(eigenvectors.round(4))
print('')

print('PCA manually calculated')
print(pca_manual.round(2))
print('')

print('PCA - sklearn')
print(pca_sklearn.round(2))


import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn')

# define equations

def dydt(t, y): return np.sin(t)**2*y
def f(t): return 2*np.exp(0.5*(t-np.sin(t)*np.cos(t)))

def RK3(t, y, h):
    # compute approximations
    k_1 = dydt(t, y)
    k_2 = dydt(t+h/2, y+(h/2)*k_1)
    k_3 = dydt(t+h/2, y+h*(-k_1 + 2*k_2))

    # calculate new y estimate
    y = y + h * (1/6) * (k_1 + 4 * k_2 + k_3)
    return y

def RK4(t, y, h):
    # compute approximations
    k_1 = dydt(t, y)
    k_2 = dydt(t+h/2, y+(h/2)*k_1)
    k_3 = dydt(t+h/2, y+(h/2)*k_2)
    k_4 = dydt(t+h, y+h*k_3)

    # calculate new y estimate
    y = y + h * (1/6)*(k_1 + 2*k_2 + 2*k_3 + k_4)
    return y

# Initialization
ti = 0
tf = 5
h = 0.5
n = int((tf-ti)/h)
t = 0
y = 2
y_rk3 = 2

print("t \t\t yRK3 \t\t yRK4 \t\t f(t)")
print(f"{t:.1f} \t\t {y_rk3:4f} \t\t {y:4f} \t\t {f(t):.4f}")

t_plot = []
y_RK3 = []
y_RK4 = []
y_analytical = []

##
for i in range(1, n+1):
    t_plot.append(t)
    y_RK4.append(y)
    y_RK3.append(y_rk3)
    y_analytical.append(f(t))

    # calculate new y estimate
    y = RK4(t, y, h)
    y_rk3 = RK3(t, y_rk3, h)

    t += h
    print(f"{t:.1f} \t\t {y_rk3:4f} \t\t {y:4f} \t\t {f(t):.4f}")

t_plot.append(t)
y_RK3.append(y_rk3)
y_RK4.append(y)
y_analytical.append(f(t))

# Visualization
fig, (ax, ax2) = plt.subplots(2, 1)
ax.plot(t_plot, y_analytical, 'ro', label='Analytical solution')
ax.plot(t_plot, y_RK4, '.b', label='Fourth-order Runge-Kutta estimate')
ax.plot(t_plot, y_RK3, '.g', label='Third-order Runge-Kutta estimate')
ax.set_ylabel("y", fontsize=18)
ax.legend()

ax2.plot(t_plot, np.abs(np.array(y_analytical) -
                        np.array(y_RK4)), '.-b', label='Fourth-order Runge-Kutta')
ax2.plot(t_plot, np.abs(np.array(y_analytical) -
                        np.array(y_RK3)), '.-g', label='Third-order Runge-Kutta')
ax2.set_ylabel("Abs Error", fontsize=18)
ax2.legend()
ax2.set_xlabel("t", fontsize=18)
plt.savefig('runge_kutta_analytical.png', dpi=300, bbox_inches='tight')


import jax.numpy as np

def softmax(x):
    """
    Description:
        The softmax activation function.
    Params:
        x: np.ndarray
            The input to the activation function.
    """
    return np.exp(x) / np.sum(np.exp(x - np.max(x, axis=1, keepdims=True)), axis=1, keepdims=True)


"""ЛР 3.2, Ларькин Владимир, М8О-303Б-18"""

import numpy as np
import fire  # CLI
import matplotlib.pyplot as plt

from sem1.lab1_2.tdma import tdma_solve

def f(a, b, c, d, x):
    return a + b * x + c * (x ** 2) + d * (x ** 3)

def get_a(f):
    return [0] + [f[i] for i in range(0, len(f) - 1)]

def get_b(f, h, c):
    b = [0]
    n = len(f) - 1
    for i in range(1, n):
        b.append((f[i] - f[i - 1]) / h[i] - 1/3 * h[i] * (c[i + 1] + 2 * c[i]))
    b.append((f[n] - f[n - 1]) / h[n] - 2/3 * h[n] * c[n])
    return b

def get_c(x, f, h):
    n = len(f)
    a = [h[i - 1] for i in range(3, n)]
    b = [2 * (h[i - 1] + h[i]) for i in range(2, n)]
    c = [h[i] for i in range(2, n - 1)]
    d = np.array([3 * ((f[i] - f[i - 1]) / h[i] - ((f[i - 1] - f[i - 2]) / h[i - 1])) for i in range(2, n)])
    x = tdma_solve(np.array([
        [b[0], c[0], 0],
        [a[0], b[1], c[1]],
        [0, a[1], b[2]]
    ]), d)
    res = [0, 0, *x]
    return res

def get_d(h, c):
    d = [0]
    n = len(c) - 1
    for i in range(1, n):
        d.append((c[i + 1] - c[i]) / (3 * h[i]))
    d.append(-c[n] / (3 * h[n]))
    return d

def find_interval(points, x):
    for i in range(0, len(points) - 1):
        if points[i] <= x <= points[i + 1]:
            return i

def spline_interpolation(points, values, x):
    h = [0] + [points[i] - points[i - 1] for i in range(1, len(points))]
    c = get_c(points, values, h)
    a = get_a(values)
    b = get_b(values, h, c)
    d = get_d(h, c)

    i = find_interval(points, x)
    res = f(a[i + 1], b[i + 1], c[i + 1], d[i + 1], x - points[i])
    return res, a, b, c, d

def draw_plot(points, vals, a, b, c, d, x_star, y_star):
    x, y = [], []
    n = len(points) - 1
    for i in range(n):
        x1 = np.linspace(points[i], points[i + 1], 10, endpoint=True)
        y1 = [f(a[i + 1], b[i + 1], c[i + 1], d[i + 1], j - points[i]) for j in x1]
        x.append(x1)
        y.append(y1)

    for i in range(n):
        plt.plot(x[i], y[i], color="red")

    plt.plot(points, vals, "o")
    plt.plot(x_star, y_star, "o", color="purple", label=f"$x^*=[{x_star:.1f}, {y_star:.3f}]$")
    plt.legend()
    # plt.axis("equal")
    plt.grid(True)
    plt.savefig("plot.jpg", dpi=300)
    plt.show()

def main():
    """Интерполяция функции кубическим сплайном"""

    init_dict = {
        "x": [-2.0, -1.0, 0.0, 1.0, 2.0],
        "y": [0.13534, 0.36788, 1.0, 2.7183, 7.3891],
        "x*": -0.5
    }

    val, a, b, c, d = spline_interpolation(init_dict["x"], init_dict["y"], init_dict["x*"])
    draw_plot(init_dict["x"], init_dict["y"], a, b, c, d, init_dict["x*"], val)

    print(f"f(x* = {init_dict['x*']}) = {val}")

    print("\nКоэффициенты:")
    for title, values in zip(["a", "b", "c", "d"], [a, b, c, d]):
        print(f"{title}:", *[f"{el:10.5f}" for el in values])

if __name__ == "__main__":
    fire.Fire(main)


import numpy as np

np.random.seed(1234)

n_states = 5
n_steps = 50
tolerance = 1e-5

# Random transition matrix and state vector
P = np.random.rand(n_states, n_states)
p = np.random.rand(n_states)

# Normalize rows in P
P /= P.sum(axis=1)[:,np.newaxis]

# Normalize p
p /= p.sum()

# Take steps
for k in range(n_steps):
    p = P.T.dot(p)

p_50 = p
print(p_50)

# Compute stationary state
w, v = np.linalg.eig(P.T)

j_stationary = np.argmin(abs(w - 1.0))
p_stationary = v[:,j_stationary].real
p_stationary /= p_stationary.sum()
print(p_stationary)

# Compare
if all(abs(p_50 - p_stationary) < tolerance):
    print("Tolerance satisfied in infty-norm")

if np.linalg.norm(p_50 - p_stationary) < tolerance:
    print("Tolerance satisfied in 2-norm")


# coding: utf-8

# In[39]:

import math

def isPrime(n):
    if n == 1:
        return False;
    elif n%2 == 0 or n==2 :
        return True
    else:
        for i in range(3,int(math.sqrt(n))+1,2):
        #for i in range(2,n):
            if n % i == 0:
                return False
        return True

isPrime(13)

# In[24]:

for i in range(1,10):
    print(i)
    if isPrime(i):
        print(i,' is Prime')

# In[75]:

from datetime import datetime

t = datetime.now()
print(isPrime(1000099))
t1 = datetime.now()

print('Time taken', (t1-t).microseconds, t1-t)

# In[58]:

# Another prime generation method
import sympy
is_prime = lambda x: sympy.isprime(x)

t = datetime.now()
print(is_prime(1000099))
t1 = datetime.now()
print('Time taken', (t1-t).microseconds, t1-t)


from numpy import zeros, sin, cos, asarray, dot, empty, amax, ones, concatenate, linspace, arange
from random import seed, uniform
from scipy import linalg, io
from math import pi
from scipy.integrate import odeint
from scipy.io import loadmat, savemat
# import matplotlib.pyplot as plt
from matplotlib.pyplot import *
from scipy.special import factorial

Samples = 5000 # Number of random initial conditions for both training and testing data
ts = 0.02 # time spacing between training state measurements
tFinal = 0.3 # time horizon --- used in measuring error

NKoopman = 2 # Number of basis functions
Nstates = 2 # Number of system states
Ncontrol = 1 # Number of system inputs

timeSteps = round(tFinal/ts)+1

####### System parameters
g = 9.81 # gravitational constant
l = 1 # pendulum length

def single_pendulum(y,t, u):
    theta, dtheta = y
    f = asarray([dtheta, g/l * sin(theta) +  u ])
    return f

def Psi_k(s, u): # Evaluates basis functions Ψ(s(t_k))
    theta, dtheta = s
    psi = zeros([NKoopman,1])
    psi[0:2, 0] = s
    if NKoopman >= 3:
        psi[2, 0] = g/l * sin(theta) + u
    if NKoopman >= 4:
        psi[3, 0] = g/l * cos(theta) * dtheta
    return psi

def A_and_G(s_1, s_2, u): # Uses measurements s(t_k) & s(t_{k+1}) to calculate A and G
    A = dot(Psi_k(s_2, u), Psi_k(s_1, u).T)
    G = dot(Psi_k(s_1, u), Psi_k(s_1, u).T)
    return A, G

def new_function_error(n,t): # error bound of an arbitrary function f using its n derivatives at time t
    total_error = t**(n+1)/factorial(n+1)
    return total_error


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
BE523 Biosystems Analysis & Design
HW3 - Problem 1. Constructing a mathematical model for penicillin clearance

Created on Sat Jan 23 21:29:34 2021
@author: eduardo
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

# The penicillin concentration that will be used to find the model
P = np.array([200, 152, 118, 93, 74])
steps = len(P)
dt = 5  # time interval
t = np.linspace(0, (steps-1)*dt, steps)  # actual time vector
dP = np.zeros(steps)

# Get the decrement of penicilin in every time interval
for i in range(0, steps-1):
    dP[i] = P[i+1] - P[i]

# Plot penicillin concentration change (dP) versus penicilline concentration (Pplot)
# If the model is correct, the points should lie on a line that goes
# through the origin, so add the point (0,0) to the array
Pplot = P[:]
Pplot = np.append(Pplot, 0)
dP = np.append(dP, 0)

# Perform a linear regression with the data, the slope of the line will give us
# the constant rate of penicillin clearance in every time step (approx. 20%)
slope, intercept, r_value, p_value, std_err = stats.linregress(Pplot, dP)
print('P=', P, '\nPplot=', Pplot, '\ndP=', dP)
print("slope=", slope, "intercept=", intercept)

# # Figure 0, plotting P vs t, original data
# plt.figure(0)
# plt.title('Original data')
# plt.plot(t, P, 'kx')
# plt.legend(['data'], loc='best')
# plt.xlabel('t')
# plt.ylabel('P')

# Figure 1, plotting dP vs P
plt.figure(1)
plt.plot(Pplot, dP, 'kx', Pplot, slope*Pplot + intercept, 'b-')
plt.legend(['data', 'linear regression ($R^2$=%.2f)' % r_value**2], loc='best')
plt.xlabel('P')
plt.ylabel('dP')
# plt.savefig('p1_penicillin_linear.png', dpi=300)  # Save figure 1

# Complete the model and make a prediction
# The model has the form B(t+1) = B[0]*(slope+1)^t
Pmodel = P[0]*(slope+1)**(t/dt)

# Figure 2, plotting P (from data and model) vs t
plt.figure(2)
plt.plot(t, P, 'kx', t, Pmodel, 'r-')
plt.legend(['data', 'model prediction'], loc='best')
plt.xlabel('t (min)')
plt.ylabel('Penicillin ($\mu$ g/ml)')
# plt.savefig('p1_penicillin_model.png', dpi=300)  # Save figure 2


import numpy as np

def getCartesianPoints(rTheta, center):
    """Convert list of polar points to cartesian points

    The returned points are not rounded to the nearest point. User must do that by hand if desired.

    Parameters
    ----------
    rTheta : (N, 2) or (2,) :class:`numpy.ndarray`
        List of cartesian points to convert to polar domain

        First column is r and second column is theta
    center : (2,) :class:`numpy.ndarray`
        Center to use for conversion to cartesian domain of polar points

        Format of center is (x, y)

    Returns
    -------
    cartesianPoints : (N, 2) :class:`numpy.ndarray`
        Corresponding cartesian points from cartesian :obj:`rTheta`

        First column is x and second column is y

    See Also
    --------
    :meth:`getCartesianPoints2`
    """
    if rTheta.ndim == 2:
        x = rTheta[:, 0] * np.cos(rTheta[:, 1]) + center[0]
        y = rTheta[:, 0] * np.sin(rTheta[:, 1]) + center[1]
    else:
        x = rTheta[0] * np.cos(rTheta[1]) + center[0]
        y = rTheta[0] * np.sin(rTheta[1]) + center[1]

    return np.array([x, y]).T

def getCartesianPoints2(r, theta, center):
    """Convert list of polar points to cartesian points

    The returned points are not rounded to the nearest point. User must do that by hand if desired.

    Parameters
    ----------
    r : (N,) :class:`numpy.ndarray`
        List of polar r points to convert to cartesian domain
    theta : (N,) :class:`numpy.ndarray`
        List of polar theta points to convert to cartesian domain
    center : (2,) :class:`numpy.ndarray`
        Center to use for conversion to cartesian domain of polar points

        Format of center is (x, y)

    Returns
    -------
    x : (N,) :class:`numpy.ndarray`
        Corresponding x points from polar :obj:`r` and :obj:`theta`
    y : (N,) :class:`numpy.ndarray`
        Corresponding y points from polar :obj:`r` and :obj:`theta`

    See Also
    --------
    :meth:`getCartesianPoints`
    """
    x = r * np.cos(theta) + center[0]
    y = r * np.sin(theta) + center[1]

    return x, y

def getPolarPoints(xy, center):
    """Convert list of cartesian points to polar points

    The returned points are not rounded to the nearest point. User must do that by hand if desired.

    Parameters
    ----------
    xy : (N, 2) or (2,) :class:`numpy.ndarray`
        List of cartesian points to convert to polar domain

        First column is x and second column is y
    center : (2,) :class:`numpy.ndarray`
        Center to use for conversion to polar domain of cartesian points

        Format of center is (x, y)

    Returns
    -------
    polarPoints : (N, 2) :class:`numpy.ndarray`
        Corresponding polar points from cartesian :obj:`xy`

        First column is r and second column is theta

    See Also
    --------
    :meth:`getPolarPoints2`
    """
    if xy.ndim == 2:
        cX, cY = xy[:, 0] - center[0], xy[:, 1] - center[1]
    else:
        cX, cY = xy[0] - center[0], xy[1] - center[1]

    r = np.sqrt(cX ** 2 + cY ** 2)
    theta = np.arctan2(cY, cX)

    # Make range of theta 0 -> 2pi instead of -pi -> pi
    # According to StackOverflow, this is the fastest method:
    # https://stackoverflow.com/questions/37358016/numpy-converting-range-of-angles-from-pi-pi-to-0-2pi
    theta = np.where(theta < 0, theta + 2 * np.pi, theta)

    return np.array([r, theta]).T

def getPolarPoints2(x, y, center):
    """Convert list of cartesian points to polar points

    The returned points are not rounded to the nearest point. User must do that by hand if desired.

    Parameters
    ----------
    x : (N,) :class:`numpy.ndarray`
        List of cartesian x points to convert to polar domain
    y : (N,) :class:`numpy.ndarray`
        List of cartesian y points to convert to polar domain
    center : (2,) :class:`numpy.ndarray`
        Center to use for conversion to polar domain of cartesian points

        Format of center is (x, y)

    Returns
    -------
    r : (N,) :class:`numpy.ndarray`
        Corresponding radii points from cartesian :obj:`x` and :obj:`y`
    theta : (N,) :class:`numpy.ndarray`
        Corresponding theta points from cartesian :obj:`x` and :obj:`y`

    See Also
    --------
    :meth:`getPolarPoints`
    """
    cX, cY = x - center[0], y - center[1]

    r = np.sqrt(cX ** 2 + cY ** 2)

    theta = np.arctan2(cY, cX)

    # Make range of theta 0 -> 2pi instead of -pi -> pi
    # According to StackOverflow, this is the fastest method:
    # https://stackoverflow.com/questions/37358016/numpy-converting-range-of-angles-from-pi-pi-to-0-2pi
    theta = np.where(theta < 0, theta + 2 * np.pi, theta)

    return r, theta

def getPolarPointsImage(points, settings):
    """Convert list of cartesian points from image to polar image points based on transform metadata

    .. warning::
        Cleaner and more succinct to use :meth:`ImageTransform.getPolarPointsImage`

    .. note::
        This does **not** convert from cartesian to polar points, but rather converts pixels from cartesian image to
        pixels from polar image using :class:`ImageTransform`.

    The returned points are not rounded to the nearest point. User must do that by hand if desired.

    Parameters
    ----------
    points : (N, 2) or (2,) :class:`numpy.ndarray`
        List of cartesian points to convert to polar domain

        First column is x and second column is y
    settings : :class:`ImageTransform`
        Contains metadata for conversion from polar to cartesian domain

        Settings contains many of the arguments in :func:`convertToPolarImage` and :func:`convertToCartesianImage` and
        provides an easy way of passing these parameters along without having to specify them all again.

    Returns
    -------
    polarPoints : (N, 2) or (2,) :class:`numpy.ndarray`
        Corresponding polar points from cartesian :obj:`points` using :obj:`settings`

    See Also
    --------
    :meth:`ImageTransform.getPolarPointsImage`, :meth:`getPolarPoints`, :meth:`getPolarPoints2`
    """
    # Convert points to NumPy array
    points = np.asanyarray(points)

    # If there is only one point specified and number of dimensions is only one, then make the array a 1x2 array so that
    # points[:, 0/1] will not throw an error
    if points.ndim == 1 and points.shape[0] == 2:
        points = np.expand_dims(points, axis=0)
        needSqueeze = True
    else:
        needSqueeze = False

    # This is used to scale the result of the radius to get the appropriate Cartesian value
    scaleRadius = settings.polarImageSize[1] / (settings.finalRadius - settings.initialRadius)

    # This is used to scale the result of the angle to get the appropriate Cartesian value
    scaleAngle = settings.polarImageSize[0] / (settings.finalAngle - settings.initialAngle)

    # Take cartesian grid and convert to polar coordinates
    polarPoints = getPolarPoints(points, settings.center)

    # Offset the radius by the initial source radius
    polarPoints[:, 0] = polarPoints[:, 0] - settings.initialRadius

    # Offset the theta angle by the initial source angle
    # The theta values may go past 2pi, so they are looped back around by taking modulo with 2pi.
    # Note: This assumes initial source angle is positive
    # theta = np.mod(theta - initialAngle + 2 * np.pi, 2 * np.pi)
    polarPoints[:, 1] = np.mod(polarPoints[:, 1] - settings.initialAngle + 2 * np.pi, 2 * np.pi)

    # Scale the radius using scale factor
    # Scale the angle from radians to pixels using scale factor
    polarPoints = polarPoints * [scaleRadius, scaleAngle]

    if needSqueeze:
        return np.squeeze(polarPoints)
    else:
        return polarPoints

def getCartesianPointsImage(points, settings):
    """Convert list of polar points from image to cartesian image points based on transform metadata

    .. warning::
        Cleaner and more succinct to use :meth:`ImageTransform.getCartesianPointsImage`

    .. note::
        This does **not** convert from polar to cartesian points, but rather converts pixels from polar image to
        pixels from cartesian image using :class:`ImageTransform`.

    The returned points are not rounded to the nearest point. User must do that by hand if desired.

    Parameters
    ----------
    points : (N, 2) or (2,) :class:`numpy.ndarray`
        List of polar points to convert to cartesian domain

        First column is r and second column is theta
    settings : :class:`ImageTransform`
        Contains metadata for conversion from polar to cartesian domain

        Settings contains many of the arguments in :func:`convertToPolarImage` and :func:`convertToCartesianImage` and
        provides an easy way of passing these parameters along without having to specify them all again.

    Returns
    -------
    cartesianPoints : (N, 2) or (2,) :class:`numpy.ndarray`
        Corresponding cartesian points from polar :obj:`points` using :obj:`settings`

    See Also
    --------
    :meth:`ImageTransform.getCartesianPointsImage`, :meth:`getCartesianPoints`, :meth:`getCartesianPoints2`
    """
    # Convert points to NumPy array
    points = np.asanyarray(points)

    # If there is only one point specified and number of dimensions is only one, then make the array a 1x2 array so that
    # points[:, 0/1] will not throw an error
    if points.ndim == 1 and points.shape[0] == 2:
        points = np.expand_dims(points, axis=0)
        needSqueeze = True
    else:
        needSqueeze = False

    # This is used to scale the result of the radius to get the appropriate Cartesian value
    scaleRadius = settings.polarImageSize[1] / (settings.finalRadius - settings.initialRadius)

    # This is used to scale the result of the angle to get the appropriate Cartesian value
    scaleAngle = settings.polarImageSize[0] / (settings.finalAngle - settings.initialAngle)

    # Create a new copy of the points variable because we are going to change it and don't want the points parameter to
    # change outside of this function
    points = points.copy()

    # Scale the radius using scale factor
    # Scale the angle from radians to pixels using scale factor
    points = points / [scaleRadius, scaleAngle]

    # Offset the radius by the initial source radius
    points[:, 0] = points[:, 0] + settings.initialRadius

    # Offset the theta angle by the initial source angle
    # The theta values may go past 2pi, so they are looped back around by taking modulo with 2pi.
    # Note: This assumes initial source angle is positive
    # theta = np.mod(theta - initialAngle + 2 * np.pi, 2 * np.pi)
    points[:, 1] = np.mod(points[:, 1] + settings.initialAngle + 2 * np.pi, 2 * np.pi)

    # Take cartesian grid and convert to polar coordinates
    cartesianPoints = getCartesianPoints(points, settings.center)

    if needSqueeze:
        return np.squeeze(cartesianPoints)
    else:
        return cartesianPoints


"""
Investigate quadratic spline

Fitting a quadratic spline from one side and from the other side
(each with a support of 3) results in two different polynoms, which
are best combined by linear interpolation. The result is a cubic cardinal
spline! To me, this suggests that the Cardinal spline (with tension 0)
is the most "natural" cubic spline.
"""

import numpy as np
import visvis as vv
import pirt
from pirt import PointSet

# Input
pp = [2, 4, 3, 1]

# Interpolate1
res1 = PointSet(2)
for t in np.arange(0,1,0.01):
    c_1 = 0.5*t**2 - 0.5*t
    c0 = -t**2 + 1
    c1 = 0.5*t**2 + 0.5*t
    res1.append(t, c_1*pp[0] + c0*pp[1] + c1*pp[2])

# Interpolate2
res2 = PointSet(2)
for t in np.arange(0,1,0.01):
    c0 = 0.5*t**2 - 1.5*t + 1
    c1 = -t**2 + 2*t
    c2 = 0.5*t**2 - 0.5*t
    res2.append(t, c0*pp[1] + c1*pp[2] + c2*pp[3])

# Linearly combine
res3 = PointSet(2)
for t in np.arange(0,1,0.01):
    # NOTE: if I write this out, I'll get a cubic spline!
    c_1 = (0.5*t**2 - 0.5*t) * (1-t)
    c0 = (-t**2 + 1) * (1-t) + (0.5*t**2 - 1.5*t + 1) * t
    c1 = (0.5*t**2 + 0.5*t) * (1-t) + (-t**2 + 2*t) * t
    c2 =  (0.5*t**2 - 0.5*t) * t
    res3.append(t, c_1*pp[0] + c0*pp[1] + c1*pp[2] + c2*pp[3])

# Combine by adding
# res3 = 0.5*(res1 + res2)

# To compare, cardinal spline
resC = PointSet(2)
for t in np.arange(0,1,0.01):
    cc = pirt.get_cubic_spline_coefs(t, 0)
    resC.append(t, cc[0]*pp[0] + cc[1]*pp[1] + cc[2]*pp[2] + cc[3]*pp[3])

# Show
vv.figure(1); vv.clf()
vv.plot(res1, lc='r', lw=1)
vv.plot(res2, lc='b', lw=1)
vv.plot(res3, lc='g', lw=2)
vv.plot(resC, lc='m', lw=5, ls=':')
vv.plot([-1, 0, 1, 2],pp, lc='k', ls='--')
#
vv.legend('From the left', 'From the right', 'Combined', 'Catmull-rom')

vv.use().Run()


import numpy as np
import scipy.stats
import loaddata

separator = '---------------------------------------------------------------'
tab: str = "\t"

def less_than_high(stat_values, mean, std_dev, test_high, set_name, stat_name, set_type, unit=''):
    # how many Pokemon VALs are less than high_val?
    high = (test_high - mean) / std_dev
    p = scipy.stats.norm.cdf(high)
    expected = p * len(stat_values)
    observed = (stat_values < test_high).sum()

    print(f"{tab * 4}How many {set_type}{set_name} {stat_name} are less than {test_high}{unit}?\n")
    print(f'{tab * 5}# expected: {round(expected)}/{len(stat_values)}')
    print(f'{tab * 5}# observed: {observed}/{len(stat_values)}')
    print(f'{tab * 5}-->: {round((100 * p), 2)}%')
    print("\n")

def more_than_low(stat_values, mean, std_dev, test_low, set_name, stat_name, set_type, unit=''):
    # How many VALs are greater than low_val?
    low = (test_low - mean) / std_dev
    p = 1 - scipy.stats.norm.cdf(low)
    expected = p * len(stat_values)
    observed = (stat_values > test_low).sum()
    print(f"{tab * 4}How many {set_type}{set_name} {stat_name} are more than {test_low}{unit}?\n")
    print(f'{tab * 5}# expected: {round(expected)}/{len(stat_values)}')
    print(f'{tab * 5}# observed: {observed}/{len(stat_values)}')
    print(f'{tab * 5}-->: {round((100 * p), 2)}%')
    print("\n")

def between_low_and_high(stat_values, mean, std_dev, test_low, test_high, set_name, stat_name, set_type, unit=''):
    # How many VALs are between low_val and high_val ?
    # get z score of desired values
    low = (test_low - mean) / std_dev
    high = (test_high - mean) / std_dev
    p = scipy.stats.norm.cdf(high) - scipy.stats.norm.cdf(low)
    expected = p * len(stat_values)
    observed = ((stat_values < test_high) & (stat_values > test_low)).sum()

    print(f"{tab * 4}How many {set_type}{set_name} {stat_name} are between {test_low}{unit} and {test_high}{unit}?\n")
    print(f'{tab * 5}# expected: {round(expected)}/{len(stat_values)}')
    print(f'{tab * 5}# observed: {observed}/{len(stat_values)}')
    print(f'{tab * 5}-->: {round((100 * p), 2)}%')
    print("\n")

def run_normal_dist_vs_actual(stat_values, stat_stats, test_bounds=(0, 50), set_name='Pokemon', stat_name='', unit='',
                              modifier='', set_type=''):
    # ----------------------------------------------------------------------------------
    print(f'{separator + separator}\n')
    print(
        f'{tab * 4}- - - Pokemon Data Analysis - Normal Distribution and Verification with real data {modifier} - - -\n')
    print(f'{tab * 14}Group 18\n')
    print(f'{separator + separator}\n')
    print(f'{tab * 7}Relevant data about the figures will be printed to the console to\n'
          f'{tab * 6}conserve space, please refer to the console using the figure numbers provided.\n'
          f'{tab * 14}Thank you!\n')
    print(f'{separator + separator}\n')
    # Using standard normal distribution for quick probability estimation for all pokemon heights (m) and weights (kg)
    print(f'{separator + separator}\n')
    print(
        f"\n{tab * 3}Using standard normal distribution for quick probability estimation of {set_type}{set_name} {stat_name}{unit} {modifier}\n")
    print(f'{separator + separator}\n')
    # ----------------------------------------------------------------------------------

    mean = stat_stats.loc['mean']
    std_dev = stat_stats['std']

    for stuff in range(0, 3):
        low, high = round(np.random.uniform(test_bounds[0], test_bounds[1] / 3), 2), round(
            np.random.uniform((test_bounds[1] / 3), test_bounds[1]), 2)
        less_than_high(stat_values, mean, std_dev, high, set_name, stat_name, set_type, unit)
        more_than_low(stat_values, mean, std_dev, low, set_name, stat_name, set_type, unit)
        between_low_and_high(stat_values, mean, std_dev, low, high, set_name, stat_name, set_type, unit)

    print(f'{separator + separator}\n')
    return


"""
Summation of primes
Problem 10
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.

Find the sum of all the primes below two million.
"""

import numpy as np

n = 5000000
sieve = np.array([True for _ in range(n)])
sieve[0:2] = False
upper_bound = int(np.sqrt(n))

for i in range(2, upper_bound+1):
    if sieve[i]:
        j = i * i
        counter = 0
        while j < n:
            sieve[j] = False
            j = (i * i) + (counter * i)
            counter += 1

primes = np.where(sieve == True)[0]

idx = np.where(primes < 2000000)[0]
subset = primes[idx]
solution = sum(subset)
print('solution: ', solution)

"""
142913828922
"""


from pandas import DataFrame, Series
from numpy import mean, dot

#################
# Syntax Reminder:
#
# The following code would create a two-column pandas DataFrame
# named df with columns labeled 'name' and 'age':
#
# people = ['Sarah', 'Mike', 'Chrisna']
# ages  =  [28, 32, 25]
# df = DataFrame({'name' : Series(people),
#                 'age'  : Series(ages)})

def create_dataframe():
    '''
    Create a pandas dataframe called 'olympic_medal_counts_df' containing
    the data from the table of 2014 Sochi winter olympics medal counts.

    The columns for this dataframe should be called
    'country_name', 'gold', 'silver', and 'bronze'.

    There is no need to  specify row indexes for this dataframe
    (in this case, the rows will automatically be assigned numbered indexes).

    You do not need to call the function in your code when running it in the
    browser - the grader will do that automatically when you submit or test it.
    '''

    countries = ['Russian Fed.', 'Norway', 'Canada', 'United States',
                 'Netherlands', 'Germany', 'Switzerland', 'Belarus',
                 'Austria', 'France', 'Poland', 'China', 'Korea',
                 'Sweden', 'Czech Republic', 'Slovenia', 'Japan',
                 'Finland', 'Great Britain', 'Ukraine', 'Slovakia',
                 'Italy', 'Latvia', 'Australia', 'Croatia', 'Kazakhstan']

    gold = [13, 11, 10, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
    silver = [11, 5, 10, 7, 7, 6, 3, 0, 8, 4, 1, 4, 3, 7, 4, 2, 4, 3, 1, 0, 0, 2, 2, 2, 1, 0]
    bronze = [9, 10, 5, 12, 9, 5, 2, 1, 5, 7, 1, 2, 2, 6, 2, 4, 3, 1, 2, 1, 0, 6, 2, 1, 0, 1]

    olympic_medal_counts_df = DataFrame({
            'country' : Series(countries),
            'gold' : Series(gold),
            'silver' : Series(silver),
            'bronze' : Series(bronze),
        })

    return olympic_medal_counts_df

def avg_bronze_medal_count():
    '''
    Compute the average number of bronze medals earned by countries who
    earned at least one gold medal.

    Save this to a variable named avg_bronze_at_least_one_gold. You do not
    need to call the function in your code when running it in the browser -
    the grader will do that automatically when you submit or test it.

    HINT-1:
    You can retrieve all of the values of a Pandas column from a
    data frame, "df", as follows:
    df['column_name']

    HINT-2:
    The numpy.mean function can accept as an argument a single
    Pandas column.

    For example, numpy.mean(df["col_name"]) would return the
    mean of the values located in "col_name" of a dataframe df.
    '''

    df = create_dataframe()

    avg_bronze_at_least_one_gold =  mean(df.bronze[df.gold >= 1])

    return avg_bronze_at_least_one_gold

def avg_medal_count():
    '''
    Using the dataframe's apply method, create a new Series called
    avg_medal_count that indicates the average number of gold, silver,
    and bronze medals earned amongst countries who earned at
    least one medal of any kind at the 2014 Sochi olympics.  Note that
    the countries list already only includes countries that have earned
    at least one medal. No additional filtering is necessary.

    You do not need to call the function in your code when running it in the
    browser - the grader will do that automatically when you submit or test it.
    '''

    df = create_dataframe()

    #find mean no. of medals by each category for a country [column-wise]
    avg_medal_count_by_type = df[['gold', 'silver', 'bronze']].apply(mean)

    #find mean no. of total medals won by each country [row-wise]
    avg_medal_count_by_country = df.loc[:][['gold', 'silver', 'bronze']].apply(mean, axis=1)

    return avg_medal_count_by_type, avg_medal_count_by_country

def numpy_dot_1():
    '''
    Imagine a point system in which each country is awarded 4 points for each
    gold medal,  2 points for each silver medal, and one point for each
    bronze medal.

    Using the numpy.dot function, create a new dataframe called
    'olympic_points_df' that includes:
        a) a column called 'country_name' with the country name
        b) a column called 'points' with the total number of points the country
           earned at the Sochi olympics.

    You do not need to call the function in your code when running it in the
    browser - the grader will do that automatically when you submit or test it.
    '''

    countries = ['Russian Fed.', 'Norway', 'Canada', 'United States',
                 'Netherlands', 'Germany', 'Switzerland', 'Belarus',
                 'Austria', 'France', 'Poland', 'China', 'Korea',
                 'Sweden', 'Czech Republic', 'Slovenia', 'Japan',
                 'Finland', 'Great Britain', 'Ukraine', 'Slovakia',
                 'Italy', 'Latvia', 'Australia', 'Croatia', 'Kazakhstan']

    gold = [13, 11, 10, 9, 8, 8, 6, 5, 4, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
    silver = [11, 5, 10, 7, 7, 6, 3, 0, 8, 4, 1, 4, 3, 7, 4, 2, 4, 3, 1, 0, 0, 2, 2, 2, 1, 0]
    bronze = [9, 10, 5, 12, 9, 5, 2, 1, 5, 7, 1, 2, 2, 6, 2, 4, 3, 1, 2, 1, 0, 6, 2, 1, 0, 1]

    #1D array
    grades = [4,2,1]
    #create 2D matrix medals
    medals = [gold,silver,bronze]
    points = dot(grades, medals)
    olympic_points_df = DataFrame({
    	'country_name' : Series(countries),
    	'points' : Series(points),
    	})

    return olympic_points_df

def numpy_dot_2():
	df = create_dataframe()

	points = dot(df[['gold', 'silver', 'bronze']],[4,2,1])
	olympic_points_df = DataFrame({
		'country_name' : df.country,
		'points' : Series(points),
	})

	return olympic_points_df

#print create_dataframe()
#print avg_medal_count()
print numpy_dot_2()


#!/usr/bin/env python
# coding: utf-8

# In[2]:

# IMPORTS HERE

import numpy as np

# import matplotlib
from matplotlib import pyplot

from scipy.stats import pearsonr

# In[3]:

ageList = [23,23,27,27,39,41,47,49,50,52,54,54,56]
fatList = [9.5,26.5,7.8,17.8,31.4,25.9,27.4,27.2,31.2,34.6,42.5,28.8,33.4]

# In[4]:

ageList

# In[5]:

fatList

# In[6]:

len(ageList)

# In[7]:

len(fatList)

# In[8]:

type(ageList)

# In[9]:

npAge = np.array(ageList)

# In[10]:

npAge

# In[11]:

type(npAge)

# In[12]:

npAge.ndim

# In[13]:

npFat = np.array(fatList)

# In[14]:

npFat

# In[20]:

ageBar = npAge.sum()/len(npAge)

# In[19]:

np.sum(ageList)/len(ageList)

# In[21]:

fatBar = npFat.sum()/len(npFat)

# In[22]:

fatBar

# In[24]:

ageDif = npAge - ageBar

# In[25]:

ageDif

# In[26]:

fatDif = npFat - fatBar

# In[27]:

fatDif

# In[28]:

fatDifAgeDif = fatDif * ageDif

# In[29]:

fatDifAgeDif

# In[30]:

sumfatDifAgeDif = fatDifAgeDif.sum()

# In[31]:

sumfatDifAgeDif

# In[32]:

delA = np.sqrt(np.sum( np.square( ageDif ) ) / len(ageList))

# In[33]:

delA

# In[34]:

delB = np.sqrt(np.sum( np.square( fatDif ) ) / len(fatList))

# In[35]:

delB

# In[38]:

r = sumfatDifAgeDif / (len(ageList) * delA * delB)

# In[39]:

r

# In[42]:

scipyr,_ = pearsonr(ageList,fatList)

# In[43]:

scipyr

# In[46]:

diff = abs(r - scipyr)

# In[47]:

diff

# In[49]:

pyplot.scatter(ageList,fatList)

# In[ ]:

# In[ ]:


import numpy as np

def bottleneck0(X,Y):
    """
    Returns the dimension 0 bottleneck distance between two non-empty persistence diagrams.
    All components are assumed to be born at the beginning of the filtration,
    that is, all non-trivial points lie in the vertical axis.

    Parameters
    ----------
    X : 1D numpy array
        Non-empty array of death times for persistence diagram 1.
    Y : 1D numpy array
        Non-empty array of death times for persistence diagram 2.

    Returns
    -------
    d : float
        The bottleneck distance between X and Y.

    Authors
    --------
    Paul Samuel P. Ignacio,
    University of the Philippines Baguio,
    Governor Pack Road, Baguio City 2600, Philippines

    Jay-Anne B. Bulauan,
    University of the Philippines Baguio,
    Governor Pack Road, Baguio City 2600, Philippines

    David Uminsky,
    University of Chicago, 5801 S Ellis Ave, Chicago,IL 60637, United States

    Examples
    --------
    >>> num_points1 = 100
    >>> num_points2 = 200
    >>> X = np.random.uniform(low=0.0, high=1000.0, size=num_points1)
    >>> Y = np.random.uniform(low=0.0, high=1000.0, size=num_points2)
    >>> bottleneck0(X,Y)

    """

    #Swap if X is longer
    if len(Y) < len(X):
        X_copy = np.copy(X)
        X = Y
        Y = X_copy

    #Initialize
    X=-np.sort(-X)
    Y=-np.sort(-Y)
    d=0
    N = len(X)

    Z = abs(X-Y[0:N])
    l = np.argmax(Z)
    dtemp = Z[l]

    # Maintain an initial bijection. Systematically modify this bijection to optimize
    # the norm between matched points until the bottleneck matching is recovered.
    if N != len(Y) and dtemp < 0.5*Y[N]:
        d = 0.5*Y[N]
    elif l >= 0 and len(Z) > 1:
        while len(Z) > 1:
            k = 0.5*max(X[l],Y[l])
            if np.max(np.delete(Z,l)) <  k and k < dtemp:
                d = k
                break

            elif np.max(np.delete(Z,l)) >= k:
                if len(Z[Z>=k]) == len(np.where(np.where(Z >= k)[0] >= l)[0]):
                    d = k
                    break
                else:
                    Z=Z[0:l]
                    X=X[0:l]
                    Y=Y[0:l]
                    l=np.argmax(Z)
                    dtemp = Z[l]
                    if len(Z) == 1:
                        d = min(dtemp,0.5*max(X[l],Y[l]))
                        break
            else:
                d = dtemp
                break
    else:
        # If one of the persistence diagrams is a singleton
        d = min(dtemp,0.5*max(X[0],Y[0]))

    return d


import numpy as np
from scipy import linalg as spla
from pressio4py import logger, solvers, ode

class RosenbrockSys:
  def createResidual(self):
    return np.zeros(6)

  def createJacobian(self):
    return np.zeros((6,4))

  def residual(self, x, R):
    x1,x2,x3,x4 = x[0],x[1],x[2],x[3]
    R[0] = 10.*(x4 - x3*x3)
    R[1] = 10.*(x3 - x2*x2)
    R[2] = 10.*(x2 - x1*x1)
    R[3] = (1.-x1)
    R[4] = (1.-x2)
    R[5] = (1.-x3)

  def jacobian(self, x, J):
    x1,x2,x3 = x[0],x[1],x[2]
    J[0,2] = -20.*x3
    J[0,3] = 10.
    J[1,1] = -20.*x2
    J[1,2] = 10.
    J[2,0] = -20.*x1
    J[2,1] = 10.
    J[3,0] = -1.
    J[4,1] = -1.
    J[5,2] = -1.

class MyQRSolver:
  def computeThin(self, A):
    self.Q, self.R = np.linalg.qr(A, mode='reduced')

  def applyQTranspose(self, operand, result):
    result[:] = self.Q.T.dot(operand)

  def applyRTranspose(self, operand, result):
    result[:] = self.R.T.dot(operand)

  def solveRxb(self, b, x):
    # solve: Rx = b
    x[:] = spla.solve(self.R, b)

def test_gn_neq_rosenbrock():
  print("\n")
  logger.initialize(logger.logto.terminal)
  logger.setVerbosity([logger.loglevel.debug])

  state   = np.array([-0.05, 1.1, 1.2, 1.5])
  problem = RosenbrockSys()
  qr_s    = MyQRSolver()
  solver  = solvers.create_gauss_newton_qr(problem, state, qr_s)
  solver.setTolerance(1e-5)
  solver.solve(problem, state)
  print(state)

  gold = np.array([1.00000001567414e+00,
                   9.99999999124769e-01,
  		   9.99999996519930e-01,
                   9.99999988898883e-01])
  assert(np.allclose(gold, state))
  logger.finalize()


#!/usr/bin/python3
import sympy
import math
from prettytable import PrettyTable

def multipleRoots(x0, tol, ite):
    if (tol >= 0):
        if (ite > 0):
            tXn.append(x0)
            y0 = functionF.evalf().subs({x:x0}).evalf()
            tfXn.append("%e" % y0)
            y0p1 = functionFP1.evalf().subs({x:x0}).evalf()
            tfp1Xn.append("%e" % y0p1)
            y0p2 = functionFP2.evalf().subs({x:x0}).evalf()
            tfp2Xn.append("%e" % y0p2)
            if (y0 != 0):
                cont = 0
                error = tol + 1
                errorAbsoluto.append("")
                tIter.append(0)
                xa = x0
                while ((y0 != 0) and (y0p1 != 0) and (y0p2 != 0) and (error > tol) and (cont < ite)):
                    xn = xa - ((y0*y0p1) / ((y0p1**2) - (y0*y0p2)))
                    tXn.append(xn)
                    y0 = functionF.evalf().subs({x:xn}).evalf()
                    tfXn.append("%e" % y0)
                    y0p1 = functionFP1.evalf().subs({x:xn}).evalf()
                    tfp1Xn.append("%e" % y0p1)
                    y0p2 = functionFP2.evalf().subs({x:xn}).evalf()
                    tfp2Xn.append("%e" % y0p2)
                    error = math.fabs(xn - xa)
                    errorAbsoluto.append("%e" % error)
                    xa = xn
                    cont = cont + 1
                    tIter.append(cont)
                if (y0 == 0):
                    print (str(xa) + " is a root")
                elif (y0p1 == 0):
                    print (str(xa) + " is a root")
                elif (y0p2 == 0):
                    print (str(xa) + " is a root")
                elif (error <= tol):
                    print (str(xa) + " is an aproximate root")
                else:
                    print ("Failed the interval!")
            else:
                print (str(x0) + " is a root")
        else:
            print ("Wrong iterates!")
    else:
        print ("Tolerance < 0")
    table.add_column("n",tIter)
    table.add_column("Xn",tXn)
    table.add_column("f(Xn)",tfXn)
    table.add_column("f'(Xn)",tfp1Xn)
    table.add_column("f''(Xn)",tfp2Xn)
    table.add_column("Error",errorAbsoluto)
    print(table)

if __name__ == "__main__":
    x = sympy.Symbol('x')
    symbols = {'e':math.e,'cos':sympy.cos,'sin':sympy.sin,'ln':sympy.ln}
    functionF  = input("Enter the function f(x) = ")
    functionF = sympy.sympify(functionF,locals = symbols)
    functionFP1 = sympy.diff(functionF)
    functionFP2 = sympy.diff(functionFP1)
    x0 = float(input("Enter the initial point: "))
    tol = float(input("Enter the tolerance: "))
    ite = int(input("Enter N iteraters: "))
    table = PrettyTable()
    tIter = []
    tXn = []
    tfXn = []
    tfp1Xn = []
    tfp2Xn = []
    errorAbsoluto = []
    multipleRoots(x0, tol, ite)


import numpy as np
import scipy.ndimage
import scipy.signal

def get_freqarr_2d(nx,ny,psx, psy):
    """
       Compute frequency array for 2 D FFT transform

       Parameters
       ----------
       nx : integer
            number of samples in the x direction
       ny : integer
            number of samples in the y direction
       psx: integer
            map pixel size in the x direction
       psy: integer
            map pixel size in the y direction

       Returns
       -------
       k : float 2D numpy array
           frequency vector
    """
    kx =  np.outer(np.fft.fftfreq(nx),np.zeros(ny).T+1.0)/psx
    ky =  np.outer(np.zeros(nx).T+1.0,np.fft.fftfreq(ny))/psy
    k = np.sqrt(kx*kx + ky*ky)
    return k

def power_spectrum_2d(arr,nbins=10,psx=1,psy=1,logbins=0):
    """
    Compute 2D power spectrum of arr

    Parameters
    ----------
    arr: float 2D numpy array
         2D array for which we compute the power spectrum
    nbins: integer, optional
         number of frequency k bins (10)

    psx:  integer, optional (1)
    Returns
    -------
    kbin: float 1D numpy array
          bins in k-space
    pkbin: float 1D numpy array
          2D power spectrum for kbin
    """
    farr = np.fft.fft2(arr)/np.double(arr.size)
    nx,ny = arr.shape
    k = get_freqarr_2d(nx,ny,psx, psy)
    pk = np.double(farr * np.conj(farr))
    if logbins:
        kbinarr = np.logspace(0.0,np.log(k.max()),nbins+1)
    else:
        kbinarr = np.arange(nbins+1)/np.double(nbins)*(k.max()-k.min())
    kbin   = np.zeros(nbins+1)
    pkbin  = np.zeros(nbins+1)
    pkbins = np.zeros(nbins+1)

    for idx in range(0,nbins):
        list = np.nonzero((k > kbinarr[idx]) * (k<= kbinarr[idx+1]))
        kbin[idx+1] = np.median(k[list])
        pkbin[idx+1] = np.mean(pk[list])
        pkbins[idx+1] = np.std(pk[list])/np.sqrt(len(list[0]))
    return kbin,pkbin,pkbins

def simu_gaussian_noise(nx,ny,pk):
    """
     Simulate a 2D map assuming Gaussian noise as computed from 2D
     power spectrum

     To be DONE !!!!!
    """
    simumap = np.random.normal(0.0,1.0,[nx,ny])
    karr = get_freqarr_2d(nx,ny,psx, psy)

    return
def cross_power_spectrum_2d(arr,arr1,nbins=10,psx=1.0,psy=1.0,logbins=0):
    nx,ny = arr.shape
    nx1,ny1 = arr1.shape
    if nx1 == nx:
        if ny1 == ny:
            farr = np.fft.fft2(arr)/np.double(arr.size)
            farr1 = np.fft.fft2(arr1)/np.double(arr1.size)
            k = get_freqarr_2d(nx,ny,psx, psy)
            pk = np.double(farr * np.conj(farr1))
            if logbins:
                kbinarr = np.logspace(0.0,np.log(k.max()),nbins+1)
            else:
                kbinarr = np.arange(nbins+1)/np.double(nbins)*(k.max()-k.min())
            kbin = np.zeros(nbins+1)
            pkbin = np.zeros(nbins+1)
            for idx in range(0,nbins):
                list = np.nonzero((k > kbinarr[idx]) * (k<= kbinarr[idx+1]))
                kbin[idx+1] = np.median(k[list])
                pkbin[idx+1] = np.mean(pk[list])
                pkbins[idx+1] = np.std(pk[list])/np.sqrt(len(list[0]))
    return kbin,pkbin,pkbins

def fourier_conv_2d(arr,kernel,doshift=True):
    """
    If kernel (e.g. a Gaussian) is peaked in the center, then you
    want to have doshift=True.
    """
    farr = np.fft.fft2(arr)
    fker = np.fft.fft2(kernel)
    farr = farr * fker
    arrc = np.real(np.fft.ifft2(farr))
    if doshift:
        arrc = np.fft.fftshift(arrc)

    return arrc

def fourier_filtering_2d(arr,filt_type,par):
    farr  = np.fft.fft2(arr)
    nx,ny = arr.shape
    kx    = np.outer(np.fft.fftfreq(nx),np.zeros(ny).T+1.0)
    ky    = np.outer(np.zeros(nx).T+1.0,np.fft.fftfreq(ny))
    k     = np.sqrt(kx*kx + ky*ky)
    if filt_type == 'gauss': filter = gauss_filter_2d(k,par)
    if filt_type == 'hpcos': filter = hpcos_filter_2d(k,par)
    if filt_type == 'lpcos': filter = lpcos_filter_2d(k,par)
    if filt_type == 'bpcos': filter = bpcos_filter_2d(k,par)
    if filt_type == 'tab':   filter = table_filter_2d(k,par)
    farr    = farr * filter
    arrfilt = np.real(np.fft.ifft2(farr))
    return arrfilt

def gauss_filter_2d(k,par):
    fwhm = par
    sigma = fwhm/(2.0*np.sqrt(2.0*np.log(2)))
    filter = np.exp(-2.0*k*k*sigma*sigma*np.pi*np.pi)
    return filter

def lpcos_filter_2d(k,par):
    k1 = par[0]
    k2 = par[1]
    filter = k*0.0
    filter[k < k1]  = 1.0
    filter[k >= k1] = 0.5 * (1+np.cos(np.pi*(k[k >= k1]-k1)/(k2-k1)))
    filter[k > k2]  = 0.0
    return filter

def hpcos_filter_2d(k,par):
    k1 = par[0]
    k2 = par[1]
    filter = k*0.0
    filter[k < k1]  = 0.0
    filter[k >= k1] = 0.5 * (1-np.cos(np.pi*(k[k >= k1]-k1)/(k2-k1)))
    filter[k > k2]  = 1.0
    return filter

def bpcos_filter_2d(k,par):
    filter = hpcos_filter_2d(k,par[0:2]) * lpcos_filter_2d(k,par[2:4])
    return filter

def gauss_2d(sigma,nx,ny):
    ix =  np.outer(np.arange(nx),np.zeros(ny).T+1)-nx/2
    iy =  np.outer(np.zeros(nx)+1,np.arange(ny).T)-ny/2
    r = ix*ix+iy*iy
    fg = np.exp(-0.5*r/sigma/sigma)
    return fg

def table_filter_2d(k,par):
    from scipy import interpolate
    kbin,filterbin = par
    f = interpolate.interp1d(kbin, filterbin)
    kbin_min = kbin.min()
    kbin_max = kbin.max()

    filter = k * 0.0
    filter[(k >= kbin_min)  & (k <= kbin_max)] = f(k[(k >= kbin_min)  & (k <= kbin_max)])   # use interpolation function returned by `interp1d`
    filter[(k < kbin_min)] = filterbin[kbin == kbin_min]
    filter[(k > kbin_max)] = filterbin[kbin == kbin_max]

    return filter

def fourier_filtering_1d(arr,filt_type,par,xarr=[]):
    farr = np.fft.fft(arr)
    if len(xarr) == 0:
        nx = len(arr)         # it's strictly one dimensional!
        k  =  np.fft.fftfreq(nx)
    else:
        nx = len(arr)         # it's strictly one dimensional!
        raise Exception

        #    if filt_type == 'gauss': filter = gauss_filter_2d(k,par)
#    if filt_type == 'hpcos': filter = hpcos_filter_2d(k,par)
#    if filt_type == 'lpcos': filter = lpcos_filter_2d(k,par)
#    if filt_type == 'bpcos': filter = bpcos_filter_2d(k,par)
    if filt_type == 'tab': filter = table_filter_1d(k,par)
    farr = farr * filter
    arrfilt = np.real(np.fft.ifft(farr))
    return arrfilt

def table_filter_1d(k,par):
    from scipy import interpolate
    kbin,filterbin = par
    f = interpolate.interp1d(kbin, filterbin)
    kbin_min = kbin.min()
    kbin_max = kbin.max()

    filter = k * 0.0

    filter[(k >= kbin_min)  & (k <= kbin_max)] = f(k[(k >= kbin_min)  & (k <= kbin_max)])   # use interpolation function returned by `interp1d`
    filter[(k < kbin_min)] = filterbin[kbin == kbin_min]
    filter[(k > kbin_max)] = filterbin[kbin == kbin_max]

    return filter


import numpy as np

def versor(A,B):
    """
    Compute the unitary vector
    pointing from A to B.
    """

    # Check A and B to be of type np.array
    if type(A) != np.ndarray:
        A = np.asarray(A)
    if type(B) != np.ndarray:
        B = np.asarray(B)
    # Compute the vector pointing from A to B
    vector = B - A
    module = np.sqrt(np.sum(vector**2))
    versor = vector/module
    return versor

def versor_perp_plane(A,B,C):
    """
    Compute the unitary vector
    perpendicular to the plane
    defined by the points A, B
    and C.
    """
    # Check A, B and C to be of type np.array
    if type(A) != np.ndarray:
        A = np.asarray(A)
    if type(B) != np.ndarray:
        B = np.asarray(B)
    if type(C) != np.ndarray:
        C = np.asarray(C)
    # Vector AC
    vector1 = C - A
    # Vector AB
    vector2 = B - A
    perp_vector = np.cross(vector2, vector1)
    perp_module = np.sqrt(np.sum(perp_vector**2))
    perp_versor = perp_vector/perp_module
    return perp_versor

#if "__main__":
#   X = [0,0,0]
#   Y = [0,3,0]
#   Z = [0,0,5]
#   print(versor(X,Y))
#   print(versor_perp_plane(X,Y,Z))


"""
Source:
https://tiggerntatie.github.io/emagnet-py/offaxis/off_axis_loop.html
Used to test against implementation in this package
Included under the MIT license
"""
from scipy.special import ellipk, ellipe
from numpy import pi, sqrt, NaN

uo = 4E-7 * pi  # Permeability constant - units of H/m
Bo = lambda i, a, u=uo: i * u / 2. / a  # Central field = f(current, loop radius, perm. constant)
al = lambda r, a: r / a  # Alpha = f(radius of measurement point, radius of loop)
be = lambda x, a: x / a  # Beta = f(axial distance to meas. point, radius of loop)
ga = lambda x, r: x / r  # Gamma = f(axial distance, radius to meas. point)
Q = lambda r, x, a: (1 + al(r, a)) ** 2 + be(x, a) ** 2  #
# Q = f(radius, distance to meas. point, loop radius)
k = lambda r, x, a: sqrt(4 * al(r, a) / Q(r, x, a))  # k = f(radius, distance to meas. point, loop radius)
K = lambda k: ellipk(k ** 2.0)  # Elliptic integral, first kind, as a function of k
E = lambda k: ellipe(k ** 2.0)  # Elliptic integral, second kind, as a function of k

# On-Axis field = f(current and radius of loop, x of measurement point)
def Baxial(i, a, x, u=uo):
    if a == 0:
        if x == 0:
            return NaN
        else:
            return 0.0
    else:
        return (u * i * a ** 2) / 2.0 / (a ** 2 + x ** 2) ** 1.5

# Axial field component = f(current and radius of loop, r and x of meas. point)
def field_axial(i, a, x, r):
    if r == 0:
        if x == 0:
            return Bo(i, a)  # central field
        else:
            return Baxial(i, a, x)  # axial field
    else:  # axial component, any location
        return Bo(i, a) * \
               (E(k(r, x, a)) * ((1.0 - al(r, a) ** 2 - be(x, a) ** 2) / (Q(r, x, a) - 4 * al(r, a))) + K(k(r, x, a))) \
               / pi / sqrt(Q(r, x, a))

# Radial field component = f(current and radius of loop, r and x of meas. point)
def field_radial(i, a, x, r):
    if r == 0:
        return 0.0  # no radial component on axis!
    else:  # radial component, any location other than axis.
        return Bo(i, a) * ga(x, r) * \
               (E(k(r, x, a)) * ((1.0 + al(r, a) ** 2 + be(x, a) ** 2) / (Q(r, x, a) - 4 * al(r, a))) - K(k(r, x, a))) \
               / pi / sqrt(Q(r, x, a))


import numpy as np
import json
import math
import sys
import os
import pandas as pd

#Import a keyboard layout matrix
#parameter: layout is a .json file withthe layout defined
def import_layout(layout_file):

    layout_matrix = {}

    #Read the json file
    with open(layout_file) as data_file:
        layout_json = json.load(data_file)

    #Get the list and calculate the gaussian distribution for every key or char of the matrix
    #1. Error_Model
    for key in layout_json:
            key_neighbors = layout_json[key]
            layout_matrix[key] = gaussian_matrix(key_neighbors)

    #2. Gaussian Distribution of the Gaussian
    return layout_matrix

 #Calculate the gaussian matrix of error distribution based on a layout keyboard
def gaussian_matrix(neighbors,mean=0,variance=0.5):

    size = len(neighbors)

    result = []

    half_size = int(math.floor(size // 2))

    x = 0
    gaussian = [[0] * size for _ in range(0, size)]
    for i in range(-half_size, half_size + 1):
        y = 0
        for j in range(-half_size, half_size + 1):
            x_dist = (1 / (math.sqrt(2 * math.pi * variance))) * (
                        math.e ** ((-(i - mean) ** 2) / (2 * variance ** 2)))
            y_dist = (1 / (math.sqrt(2 * math.pi * variance))) * (
                        math.e ** ((-(j - mean) ** 2) / (2 * variance ** 2)))
            gaussian[x][y] = x_dist * y_dist
            y += 1
        x += 1

    probability = gaussian
    for i in range(0, size):
        for j in range(0, size):
            if neighbors[i][j] is not None:
                result += [(neighbors[i][j], probability[i][j])]

    total = sum(prob for neighbor, prob in result)
    normalized_result = [(neighbor, prob / total) for neighbor, prob in result]

    return normalized_result

#Check if this is correct and try to do some eperiments
def create_emission_matrix(errors_distributions):
    size = len(errors_distributions)
    epsilon=10*10**-5
    emission_matrix = np.full((size, size), epsilon, dtype=float)
    key_list = []
    keys_list = list(errors_distributions.keys())
    keys_list.sort()
    map_to_zero = list(zip(keys_list, range(0, size)))
    map_to_zero = dict(map_to_zero)

    for key in errors_distributions:
        key_list += [key]
        for letter, probability in errors_distributions[key]:
            i = map_to_zero[key]
            j = map_to_zero[letter]
            emission_matrix[i, j] = probability

    for i in range(0, size):
        emission_matrix[i] = emission_matrix[i] / sum(emission_matrix[i])

    result = np.matrix(emission_matrix)

    observations = list(errors_distributions.keys())
    observations.sort()

    return observations, result

def generate_emission_matrix(layout_file,out_file=None):

    result = import_layout(layout_file)

    for key in result:
        a = sum(n for _, n in result[key])

    observations, emission = create_emission_matrix(result)

    if(not(out_file == None)):
        np.savetxt(out_file, emission, delimiter=';')

    return observations, emission


import numpy as np  # calculations
import pathlib  # needed to create folder
import matplotlib.pyplot as plt  # needed for graphs
import sympy as sp
from sympy.abc import E, e, n, M, o

print('Program started running!')

dpisiz = 1000

def savim(dir, name):
    path = pathlib.Path(f"./{dir}")
    path.mkdir(exist_ok=True, parents=True)
    plt.savefig(f'./{dir}/{name}.png', dpi=dpisiz)
    print(f'Saved image location: ./{dir}/{name}.png')

fract = e ** n / sp.factorial(n)
func = (sp.sin(M)) ** n
dif_func = (sp.Derivative(func, (M, n - 1)))
sumo = sp.Sum(fract * dif_func, (n, 1, o))
sum3 = (sumo.subs(o, 3)).doit()
sum10 = (sumo.subs(o, 10)).doit()
E3 = sum3 + M
E10 = sum10 + M
En3_e3 = sp.trigsimp(E3.subs(e, 0.3))
En10_e3 = sp.trigsimp(E10.subs(e, 0.3))
En3_e9 = sp.trigsimp(E3.subs(e, 0.9))
En10_e9 = sp.trigsimp(E10.subs(e, 0.9))

# Use sp.lambdify to create our lambda functions
# and plot our graphs. Could have used sp.plot but I prefer having more options with matplotlib.

numberoftries = 10000
xx = np.linspace(0, 2 * np.pi, 10000)
En3_e3f = sp.lambdify(M, En3_e3)(xx)
En10_e3f = sp.lambdify(M, En10_e3)(xx)
En3_e9f = sp.lambdify(M, En3_e9)(xx)
En10_e9f = sp.lambdify(M, En10_e9)(xx)

fig = plt.figure()
ax = plt.axes()

# Import data from task 1 to compare

data = np.genfromtxt(fname="data.csv", delimiter=',')
ax.set_ylabel('E')
ax.set_xlabel('M')
ax.plot(data[:, 0], data[:, 2], label='E for e=0.3', color='tab:pink')
ax.plot(xx, En3_e3f, label='E for n=3, e=0.3', linestyle='dashed', color='tab:red')
ax.plot(xx, En10_e3f, label='E for n=10, e=0.3', linestyle='dotted', color='tab:blue')
ax.legend()
savim('pr1_task2', 'e_03')
fig1 = plt.figure()
ax1 = plt.axes()
ax1.plot(data[:, 0], data[:, 5], label='E for e=0.9', color='black')
ax1.plot(xx, En3_e9f, label='E for n=3, e=0.9', color='tan')
ax1.plot(xx, En10_e9f, label='E for n=10, e=0.9', color='fuchsia')
ax1.legend()
ax1.set_ylabel('E')
ax1.set_xlabel('M')
savim('pr1_task2', 'e_09')

print('Program Finished running!')
# As we can see from our results, for lower eccentricities both n=3 and n=10 are nearly identical
# compared to the Newton-Raphson method. However for 0.9, this is no longer the case.
# In this case there are some sinusoidal curves with small amplitude appearing except the
# middle of the curve (There are still some curvature showing at the middle,
# however it’s close to the Newton-Raphson method).


#!/usr/bin/env python
#!-*- coding: utf-8 -*-

import numpy as np

__author__ = "Irshad Ahmad Bhat"
__version__ = "1.0"
__email__ = "irshad.bhat@research.iiit.ac.in"

class Distance():

    def euclidean(self, X, Y):

	"""Returns Euclidean distance between to n-dimentional vectors."""

	X = np.asarray(X)
	Y = np.asarray(Y)

	return np.sqrt(np.sum((X-Y)**2))

    def manhattan(self, X, Y):

	"""Returns Euclidean distance between to n-dimentional vectors."""

	X = np.asarray(X)
	Y = np.asarray(Y)

	return np.sum(np.abs(X-Y))

    def cosine(self, X, Y):

	"""Returns Cosine distance between to n-dimentional vectors."""

	X = np.asarray(X)
	Y = np.asarray(Y)

	return 1 - np.sum(X*Y) / (np.sqrt(np.sum(X**2)) * np.sqrt(np.sum(Y**2)))