import numpy as np
import sys

def integration_alg(func, lower_bound, upper_bound, number_of_steps):
    """

    :param func: function to use in integration, when given a radius, will return the value at that point
    :param lower_bound: lower bound of integration
    :param upper_bound: upper bound of integration
    :param number_of_steps: number of steps to do
    :return:
    """

    # Current method if the midpoint rule, as the function is an improper integral from the lower bound being 0

    # Need to integrate from lower_bound to upper_bound in number_of_steps

    # lower bound is 0, if there is a radius of 0, then no satallites in it
    integration_value = 0
    step_size = (upper_bound - lower_bound) / number_of_steps  # The number of steps to take
    for i in range(number_of_steps):
        if i != 0:
            # Current step can be just i*step_size but only if integral always starts at 0
            # since it might not, need a starting point otherwise:
            current_step = lower_bound + i * step_size
            prev_step = lower_bound + (i - 1) * step_size

            # Current midpoint is the current step + the prev step divided by 2
            # F(mk) where mk = (tk + tk-1)/2
            current_midpoint = (current_step + prev_step) / 2
            integration_value += func(current_midpoint)

    # Last bit is the multiplication by the step size to get the full value
    integration_value *= step_size

    return integration_value

def differentiation_alg(func, b, integral, eps=1e-12):
    """
    Numerical differentiation at point by calculating deriviative at a point and halving step size

    :param func:
    :param b:
    :param eps:
    :return:
    """

    h = 0.01

    # First step
    def calc_deriv():
        return (func(b + h / 2, integral) - func(b - h / 2, integral)) / h

    prev_deriv = calc_deriv()

    while True:
        h = h / 2  # Cut step size in half each iteration
        deriv = calc_deriv()
        if abs(deriv - prev_deriv) < eps:  # Means it has converged
            return deriv
        else:
            prev_deriv = deriv

def part_b():
    H0 = 7.16e-11
    Omega_M = 0.3
    Omega_Lambda = 0.7

    def a_from_z(z):
        """
        Gets a from z
        :param z:
        :return:
        """
        return 1 / (z + 1)

    def integrand(a):
        """
        Integrand for the LGF
        :param a:
        :return:
        """
        return (1 / a ** 3) / (Omega_M / a ** 3 + Omega_Lambda) ** 1.5

    def H(a):
        """
        Hubble Constant with respect to a
        :param a:
        :return:
        """
        return H0 * np.sqrt(Omega_M * (1 / a) ** 3 + Omega_Lambda)

    def D_a(a, integral):
        """
        Linear Growth Factor D(a) with precalced integral
        :param a:
        :param integral:
        :return:
        """
        return 2.5 * Omega_M * (Omega_M / a ** 3 + Omega_Lambda) ** 0.5 * integral

    a0 = 0
    final_a = a_from_z(50)  # z = 50, a = 1/(z+1) (z+1) = 1/a
    integral = integration_alg(integrand, a0, final_a, 20000)
    sys.stdout = open('4b.txt', 'w')
    numerical_deriv = final_a * H(final_a) * differentiation_alg(D_a, b=final_a, integral=integral)
    print('Numerical d/da at z = 50: {}'.format(numerical_deriv))
    analytic_deriv = -15 / 4 * Omega_M ** 2 * H0 * integral / final_a ** 3
    print('Analytical d/da at z = 50: {}'.format(analytic_deriv))


import numpy as np
from scipy.stats import linregress
from matplotlib import pyplot as pl

def spheres(n = 10):
    x = np.zeros(n)
    y = np.zeros(n)
    for i in range(n):
        x[i] = i + 1.0
        y[i] = np.pi / 6 * x[i]**3

    return (x, y)

def DINpaper(width0 = 1./np.sqrt(3), n = 10):
    a, b, c = width0, np.sqrt(2)*width0, 1
    x = np.zeros(n)
    y = np.zeros(n)
    for i in range(n):
        x[i] = np.sqrt(a*a+b*b)
        y[i] = c
        a, b, c = b, b*np.sqrt(2), c*2

    return (x, y)

def DINpaper3D(width0 = 1./np.sqrt(3), n = 10):
    a, b, c = width0, np.sqrt(2)*width0, 1
    x = np.zeros(n)
    y = np.zeros(n)
    for i in range(n):
        x[i] = np.sqrt(2*a*a+b*b)
        y[i] = c
        a, b, c = b, b*np.sqrt(2), c*np.sqrt(8)

    return (x, y)

def DINpaperS():
    x = np.array((2.2, 3, 4, 5.5, 7.1, 10))
    y = np.append((1,), np.cumprod(2*np.ones(len(x)-1)))
    return (x, y)

def show(title, func, **kwargs):
    x, y = func(**kwargs)
    fit = linregress(np.log10(x), np.log10(y))
    d = fit.slope
    p = 10**fit.intercept
    pl.title(title)
    pl.loglog(x, y, 'ko--',
            markersize = 9,
            markerfacecolor = 'none',
            markeredgewidth = 2,
            lw = 2,
            label = 'data')
    pl.loglog(x, p*x**d, 'r-', lw = 1, label = 'fit: y~x^d, d = %.2f' % d)
    pl.xlabel('Size')
    pl.ylabel('Mass')
    pl.grid()
    pl.legend(loc = 'upper left')
    pl.show()

def main(title):
    if title == '3D Spheres':
        show(title, spheres)

    if title == '2D paper':
        show(title, DINpaper)
        #show(title, DINpaper, n=100)

    if title == '3D paper':
        show(title, DINpaper3D)

    if title == 'Crumpled paper balls':
        show(title, DINpaperS)

if __name__ == '__main__':
    main('3D Spheres')
    main('2D paper')
    main('3D paper')
    main('Crumpled paper balls')


import math
import random
import sympy

class RSA:

    def __init__(self):
        self.primes: tuple = None
        self.modulus: int = None
        self.totatives: list = None
        self.encryption_key: int = None
        self.decryption_key: int = None
        self.public_key: tuple = None
        self.private_key: tuple = None

    def get_primes(self):
        """ Generates prime numbers for the public and private key creation

        Returns:

        """
        prime1 = sympy.prime(random.randint(5, 500))  # Use arbitrary min and max primes
        prime2 = sympy.prime(random.randint(5, 500))
        self.primes = (prime1, prime2)
        self.modulus = prime1 * prime2

    def get_encryption_key(self):
        """ Gets the encryption key

        Returns:

        """
        if self.modulus is None:
            self.get_primes()

        # Get the upper bound of the encryption number range
        key_range = len(self.totatives)

        # Get the encryption key
        encryption_key = None
        while encryption_key is None:
            # Key must be in the range 1<e<len(phi(n)) and must be coprime to both n and phi(n)
            # 2 won't satisfy the criteria for any number, so 3 is the lower bound
            rand_int = random.randint(3, key_range - 1)
            if math.gcd(rand_int, key_range) == 1 and rand_int in self.totatives:
                encryption_key = rand_int

        self.encryption_key = encryption_key
        self.public_key = (encryption_key, self.modulus)

    def get_decryption_key(self, randomize: bool = False):
        """ Returns the decryption key

        Args:
            randomize: True to randomize the decryption value instead of using the first valid value

        Returns:

        """
        decryption_key = None
        temp = 1

        # Use the nth valid value of the decryption key if randomize is True
        nth_value = random.randint(1, 500)  # Use an arbitrary limit for testing purposes
        valid_value_count = 0

        while decryption_key is None:
            # Formula: solve for d where de(mod(phi(n)) = 1. e = encryption key
            if (temp * self.encryption_key) % len(self.totatives) == 1:
                if randomize:
                    valid_value_count += 1
                    if valid_value_count >= nth_value:
                        decryption_key = temp
                else:
                    decryption_key = temp
            temp += 1

        self.decryption_key = decryption_key
        self.private_key = (decryption_key, self.modulus)

    def get_totatives(self, n):
        """ Gets the positive integers up to n that are relatively prime to n, aka Euler's totient function

        Args:
            n: Integer to get totatives of

        Returns:

        """
        if n < 1 or not isinstance(n, int):
            raise ValueError

        totatives = []
        for k in range(1, n+1):
            if math.gcd(n, k) == 1:
                totatives.append(k)

        self.totatives = totatives

    def make_keys(self):
        """ Generates public and private keys

        Returns:

        """
        self.get_primes()
        self.get_totatives(self.modulus)
        self.get_encryption_key()
        self.get_decryption_key()

if __name__ == '__main__':
    rsa = RSA()
    rsa.make_keys()


import numpy as np

def exercise_11_3():
    np.set_printoptions(precision=16)
    m,n=50,12

    # choose 50 linearly npaced grid points on the interval [0,1]
    ls=np.linspace(0,1,m)

    # evaluate 11th degree polynomial at grid points
    A=np.array([[ls[i]**j for j in range(n)] for i in range(m)])

    # evaluate function at gridpoints
    b=np.transpose(np.cos(4*ls))

    # solve normal equation
    At=np.transpose(A)
    parta=np.linalg.solve(At@A,At@b)

    # QR then solve
    Q,R=np.linalg.qr(A,mode='reduced')
    partd=np.linalg.solve(R,np.transpose(Q)@b)[0]

    # solve least squares directly
    parte=np.linalg.lstsq(A,b)[0]

    # SVD then solve
    U,s,Vh=np.linalg.svd(A,full_matrices=False)
    w=np.linalg.lstsq(np.diag(s),np.transpose(U)@b)[0]
    partf=np.transpose(Vh)@w

    print(np.transpose([parta,partd,parte,partf]))

def exercise_4_4():
    A=np.array(
            [[1,-1,0,0],
             [-1,0,1,0],
             [1,0,0,-1],
             [0,0,1,-1],
             [0,1,0,-1],
             [1,1,1,1]]
            )
    b=np.transpose(np.array([[4,9,6,3,7,20]]))
    At=np.transpose(A)
    x=np.linalg.solve(At@A,At@b)
    print(x)

exercise_11_3()
exercise_4_4()

# returns Q,R
def givens_qr(A):
    np.set_printoptions(precision=5)
    m,n=np.shape(A)
    E=np.identity(m)
    for i in range(m-1):
        v=A[i:,i]
        k=m-i
        Qk=np.identity(k)
        for j in range(1,k):
            theta=np.arctan2(v[k-j],v[k-j-1])
            Qj=np.identity(k)
            Qj[k-j-1:k-j+1,k-j-1:k-j+1]=np.array([[np.cos(theta),np.sin(theta)],[-np.sin(theta),np.cos(theta)]])
            v=Qj@v
            Qk=Qj@Qk

        Ek=np.identity(m)
        Ek[m-k:m,m-k:m]=Qk

        E=Ek@E
        A=Ek@A

    return(A,np.transpose(E))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Ejemplo 3: aproximación de una función

Jupyter notebook

autor: Ramiro Irastorza
"""

from __future__ import print_function
import numpy as np #importo numpy y lo denomino np
import matplotlib.pyplot as plt

#Definimos la función sombrero
def phi(x,xi,i):
    hi = xi[1]-xi[0]
    f = np.zeros_like(x)
    for m in range(len(x)):
        if i == 0:
            if xi[i] <= x[m] < xi[i+1]:
                f[m] = (xi[i+1]-x[m])/(xi[i+1]-xi[i])
            else:
                f[m] = 0.0
        elif i == len(xi):
            if (xi[i-1] < x[m] <= xi[i]):
                f[m] = (x[m]-xi[i-1])/(xi[i]-xi[i-1])
            else:
                f[m] = 0.0
        else:
            if (xi[i-1] < x[m] <= xi[i]):
                f[m] = (x[m]-xi[i-1])/(xi[i]-xi[i-1])
            elif xi[i] < x[m] <= xi[i+1]:
                f[m] = (xi[i+1]-x[m])/(xi[i+1]-xi[i])
            else:
                f[m] = 0.0

    return f

#Puntos de x0 a xnx
nx = 5 #numero de intervalos
nodos = nx+1 #cantidad de nodos
x = np.linspace(0,1,200) #continuo
y = 2.0*x*np.sin(2.0*np.pi*x)+3.0 #funcion
xi = np.linspace(0,1,nodos) #nodos equiespaciados
vi = 2.0*xi*np.sin(2.0*np.pi*xi)+3.0 #valores en los nodos

v = vi[0]*phi(x,xi,0)+vi[1]*phi(x,xi,1)+vi[2]*phi(x,xi,2)+vi[3]*phi(x,xi,3)+vi[4]*phi(x,xi,4)+vi[5]*phi(x,xi,5)

plt.rc('text', usetex=True)
plt.rc('font', family='serif')
fig, axs = plt.subplots(1,1)
axs.plot(xi,vi,'ro',markersize=10)
axs.plot(x,v,'r')
axs.plot(x,y,'b')
axs.fill_between(x, y, v)
axs.set_ylim(-1,5)
axs.axhline(0, color='gray')
axs.vlines(xi[0],vi[0],0,linestyles='dashed')
axs.annotate(r'$x_{0}$', xy=(xi[0]-0.02, -0.5),fontsize=16)
axs.vlines(xi[-1],vi[-1],0,linestyles='dashed')
axs.annotate(r'$x_{n}$', xy=(xi[-1]-0.02, -0.5),fontsize=16)

plt.show()


import numpy as np

from . _modulated_utilits import _check_size
__all__ = ['modulated_signal']

#------------------------------------------------------------------------------------
def modulated_signal(frequencies,
                     amplitudes,
                     init_phases,
                     length=None,
                     fs=None,
                     delay = 0,
                     ret_complex = True):
    '''
    Build one-mode arbitarry modulated signal.
    s(n) = a(n)exp(1j[phi(n)+phi0(n)]),
        phi(n) = 2pi*integral(*f(n)*n/fs)

    Paramters
    -----------
    frequencies: float or 1d ndarray,
      could be either carier frequency (float),
      or frequency to time relation (frequency modulation).
    amplitudes: float or 1d ndarray,
      could be either amplitude level (float),
      or amplitude to time relation (amplitude modulation).
    init_phases: float or 1d ndarray,
      could be either initial phase (float),
      or additional phase to time relation (phase modulation).
    length: int or None,
      length or required signal,
      if None, length = max of lengths
      between frequencies, amplitudes, init_phases
    fs: float or None,
      sampling frequency, if None, fs = length
    delay: float,
      if delay>0, then add shift in signal start: int(delay*fs),
      and additonal initail phase shift:
        2*np.pi*ddelay*frequencies[0],
        ddelay = (delay*fs-int(delay*fs))/fs,
    ret_complex: bool,
      if true - complex valued signal will be returned.

    Returns
    --------------
    signal: 1d ndarray.

    Note
    ----------
    The Result is similar to Intrinsic-mode-function.
    If some of the parameters (freq, amp, phase) have a size,
      smaller than the required size, it will be extended,
      with the last value. In the opposite case,
      it will be cut up to the required size.
    This function work with only one signal,
      if you want superposition of signals,
      make sum of severeal function calls.

    Examples
    ----------------
    import numpy as np
    import dsatools.utilits as ut
    from dsatools.generator import modulated_signal
    N = 512
    FS = 20
    f0 = 1
    delta_f = 0.3
    freq = f0+delta_f*np.arange(N)/N
    s = modulated_signal(freq,
                         amplitudes = 1,
                         init_phases = 0,
                         length=N,
                         fs=FS,
                         delay = 13.043/FS,
                         ret_complex = True)
    ut.probe(s)

    '''
    frequencies = np.asarray(frequencies, dtype = float)
    amplitudes  = np.asarray(amplitudes,  dtype = float)
    init_phases = np.asarray(init_phases, dtype = float)

    if length is None:
        length = max(amplitudes.size,
                     frequencies.size,
                     init_phases.size)

    if fs is None:
        fs = length

    dt = 1/fs

    frequencies = _check_size(frequencies, length)
    amplitudes  = _check_size(amplitudes,  length)
    init_phases = _check_size(init_phases, length)

    full_phase = 2*np.pi*np.cumsum(frequencies)*dt+init_phases

    if delay>0:
        n_delay = int(delay*fs)
        ddelay  = (delay*fs-n_delay)/fs
        dphi0   =  2*np.pi*ddelay*frequencies[0]
        amplitudes = np.concatenate((np.zeros(n_delay),
                                     amplitudes[:-n_delay]))
        full_phase = np.concatenate((np.zeros(n_delay),
                                     full_phase[:-n_delay]+dphi0))

    if(ret_complex):
        return amplitudes*np.exp(1j*full_phase)
    else:
        return amplitudes*np.cos(full_phase)


from django.shortcuts import render
import numpy as np
from sympy import *

def newtonRaphson(f, fderivative, xn, e, N):
    imprimir=['','','']
    resultados=[]
    x = symbols('x')
    step = 1
    flag = 1
    condition = True
    while condition:
        if np.float64(fderivative.evalf(subs={x: xn})) == 0.0:
            imprimir[0]='Error de division entre 0!'
            break
        x0 = xn
        fx0=np.float64(f.evalf(subs={x: xn}))
        xn = xn - np.float64(f.evalf(subs={x: xn})) / \
            np.float64(fderivative.evalf(subs={x: xn}))
        partes=[]
        partes.append(step)
        partes.append('x0 =%0.6f'%x0)
        partes.append('f(x0) =%0.6f'%fx0)
        partes.append('x1 =%0.6f'%xn)
        partes.append('f(x1) =%0.6f'%np.float64(f.evalf(subs={x: xn})))
        resultados.append(partes)
        step += 1
        if step > N:
            flag = 0
            break
        condition = abs(np.float64(f.evalf(subs={x: xn}))) > e
    imprimir[1]=resultados
    if flag == 1:
        imprimir[2]='\nRaiz requerida es: %0.8f' % xn
    else:
        imprimir[2]='\nNo convergente.'
    return imprimir

def biseccion(f,xa,xb,e,N):
    imprimir=['','','']
    resultados=[]
    x = symbols('x')
    step = 1
    flag = 1
    condition = True
    ans_xr=0
    while condition:
        xr = (xa+xb)/2
        fxa=np.float64(f.evalf(subs={x: xa}))
        fxr=np.float64(f.evalf(subs={x: xr}))
        partes=[]
        partes.append(step)
        partes.append('xa=%0.6f'%xa)
        partes.append('xb=%0.6f'%xb)
        partes.append('xr=%0.6f'%xr)
        partes.append('f(xa)=%0.6f'%fxa)
        partes.append('f(xr)=%0.6f'%fxr)
        if len(resultados)>0:
            ex=abs((xr-ans_xr)/xr)
            condition = ex > e
        else:
            ex=0
        partes.append('E(x)=%0.6f'%(ex*100))
        resultados.append(partes)

        if (fxa*fxr)>0:
            xa=xr
            xb=xb
        else:
            xa=xa
            xb=xr
        step += 1
        ans_xr=xr
        if step > N:
            flag = 0
            break
    imprimir[1]=resultados
    if flag == 1:
        imprimir[2]='\nRaiz requerida es: %0.8f' % xr
    else:
        imprimir[2]='\nNo convergente.'
    return imprimir

def BiseccionView(request):
    if request.method == 'POST':
        f = parse_expr(request.POST.get("funcion"))
        xa = np.float64(request.POST.get("puntoxa"))
        xb = np.float64(request.POST.get("puntoxb"))
        e = np.float64(request.POST.get("error"))
        N = np.float64(request.POST.get("pasos"))

        imprimir = biseccion(f,xa,xb,e,N)
        ctx ={
            'error':imprimir[0],
            'iteraciones':imprimir[1],
            'resultado':imprimir[2],
            'err':request.POST.get("error"),
            'puntoxa':request.POST.get("puntoxa"),
            'puntoxb':request.POST.get("puntoxb"),
            'funcion':request.POST.get("funcion"),
            'pasos':request.POST.get("pasos")
        }

    else:
        ctx = {}

    return render(request, 'ecuacionesNoLineales/Biseccion/biseccion.html', ctx)

# Create your views here.
def NewtonView(request):
    if request.method == 'POST':
        x = symbols('x')
        f = parse_expr(request.POST.get("funcion"))
        fderivative = f.diff(x)

        xn = np.float64(request.POST.get("punto"))
        e = np.float64(request.POST.get("error"))
        N = np.float64(request.POST.get("pasos"))
        imprimir = newtonRaphson(f, fderivative, xn, e, N)
        ctx ={
            'error':imprimir[0],
            'iteraciones':imprimir[1],
            'resultado':imprimir[2],
            'err':request.POST.get("error") ,
            'punto':request.POST.get("punto"),
            'funcion':request.POST.get("funcion"),
            'pasos':request.POST.get("pasos")
        }
    else:
        ctx = {}

    return render(request, 'ecuacionesNoLineales/Newton-Raphson/newton.html', ctx)

def homeECN(request):
    return render(request, 'ecuacionesNoLineales/nolineal.html', {})


"""
Created on Tuesday 22 August 2017
Last update: Thursday 18 January 2018

@author: Michiel Stock
michielfmstock@gmail.com

Functions for quadratic optimization
"""

import numpy as np

def solve_1d_quadratic(p, q, r=0):
    """
    Finds the minimizer of an 1-D quadratic system, raises an error if there is
    no minimizer (p<0)

    Inputs:
        - p, q, r: the coefficients of the 1D quadratic system

    Output:
        - xstar: the minimizer
    """
    assert p > 0
    return - q / p

def compute_quadratic(P, q, r, x):
    """
    Evaluates a quadratic function

    Inputs:
        - P, q, r: the terms of the nD quadratic system
        - x: point at which the quadratic function is evaluated

    returns
        - f(x)
    """
    return np.sum(x.T @ P @ x / 2 + x.T @ q + r)

def solve_nd_quadratic(P, q, r=0):
    """
    Finds the minimizer of an N-D quadratic system,
    raises an error if there is no minimizer
    (P is not positive-definite)

    Inputs:
        - P, q, r: the terms of the nD quadratic system

    Output:
        - xstar: the minimizer, an (n x 1) vector
    """
    assert np.all(np.linalg.eigvalsh(P) > 0)
    return - np.linalg.solve(P, q)

def quadratic_exact_line_search(P, q, Dx, x):
    """
    Find the exact step size that minimized a quadratic system in
    a given point x for a given search direction Dx

    Inputs:
        - P, q: the terms of the nD quadratic system
        - x: starting point
        - Dx: search direction

    Output:
        - t: optimal step size
    """
    PDx = P @ Dx
    DxTPDx = np.sum(PDx * Dx)
    DxTq = np.sum(Dx * q)
    DxTPx = np.sum(PDx * x)
    t_optimal =  - (DxTPx + DxTq) / DxTPDx
    return t_optimal

def gradient_descent_quadratic(P, q, x0, epsilon=1e-4, trace=False):
    """
    Gradient descent for quadratic systems

    Inputs:
        - P, q: the terms of the nD quadratic system
        - x0: starting point
        - trace: (bool) count number of steps?

    Outputs:
        - xstar: the found minimum
        - n_steps: number of steps before algorithm terminates (if trace=True)
    """
    x = x0  # initial value
    n_steps = 0
    while True:
        Dx = - P @ x - q
        if np.linalg.norm(Dx) < epsilon:
            break
        t = quadratic_exact_line_search(P, q, Dx, x)
        x += t * Dx
        n_steps += 1
    if trace: return x, n_steps
    else: return x

def gradient_descent_quadratic_momentum(P, q, x0,
                                        beta=0.2, epsilon=1e-4,
                                        trace=False):
    """
    Gradient descent for quadratic systems with momentum

    Inputs:
        - P, q: the terms of the nD quadratic system
        - x0: starting point
        - beta: momentum parameter (default set to 0.2)
        - trace: (bool) count number of steps?

    Outputs:
        - xstar: the found minimum
        - n_steps: number of steps before algorithm terminates (if trace=True)
    """
    x = x0  # initial value
    n_steps = 0
    Dx = np.zeros_like(x)
    while True:
        gradient = P @ x + q
        Dx *= beta
        Dx -= gradient * (1 - beta)
        if np.linalg.norm(gradient) < epsilon:
            break
        t = quadratic_exact_line_search(P, q, Dx, x)
        x += t * Dx
        n_steps += 1
    if trace: return x, n_steps
    else: return x

if __name__ == '__main__':

    # assignment 1

    print('solution 1d stystem: xstar={}'.format(
            solve_1d_quadratic(8, 8, 2)
    ))

    # example system

    P = np.array([[4,1],[1,2]]) * 2
    q = np.array([[3], [1]])
    r = 12
    x0 = np.zeros((2, 1))
    #x0 = np.array([[1.0], [1.0]])

    def fun_quadr(x):
        return np.sum(x.T @ P @ x) / 2 + np.sum(q * x) + r

    # assignment 2

    xstar_exact = solve_nd_quadratic(P, q)
    print('analytic solution nd system: xstar_exact={}'.format(xstar_exact))

    # assignment 4
    xstar_gd, n_steps_gd= gradient_descent_quadratic(P, q,
                        x0.copy(), trace=True)
    print('solution using GD: xstar_exact={} ({} steps)'.format(
                                    xstar_exact, n_steps_gd))

    # assignment 5
    xstar_gdm, n_steps_gdm = gradient_descent_quadratic_momentum(P, q,
                        x0.copy(), beta=0.2, trace=True)
    print('solution using GDM: xstar_exact={} ({} steps)'.format(xstar_exact,
                            n_steps_gdm))

    # new problem

    P = np.array([[500, 2], [2, 1]])
    q = np.array([-40, 100]).reshape((-1,1))
    x0 = np.zeros((2, 1))

    print('new values:')
    xstar_gd, n_steps_gd= gradient_descent_quadratic(P, q,
                        x0.copy(), trace=True)
    print('solution using GD: xstar={} ({} steps)'.format(
                                    xstar_gd, n_steps_gd))

    xstar_gdm, n_steps_gdm = gradient_descent_quadratic_momentum(P, q,
                        x0.copy(), beta=0.2, trace=True)
    print('solution using GDM: xstar={} ({} steps)'.format(xstar_gdm,
                            n_steps_gdm))


#!/usr/bin/env python3
"""
Dimensionality Reduction
"""
import numpy as np

def HP(Di, beta):
    """
    Calculates the Shannon entropy and P affinities relative to a data point

    Di is a numpy.ndarray of shape (n - 1,) containing the pariwise distances
    between a data point and all other points except itself

        n is the number of data points

    beta is a numpy.ndarray of shape (1,) containing the beta value for the
    Gaussian distribution

    Returns: (Hi, Pi)

    Hi: the Shannon entropy of the points
    Pi: a numpy.ndarray of shape (n - 1,) containing the P affinities of the
    points
    """
    Pi = (np.exp(-Di * beta)) / (np.sum(np.exp(-Di * beta)))
    Hi = - np.sum(Pi * np.log2(Pi))

    return (Hi, Pi)


"""
  Smoothing of Spectra using Whittaker-Handerson Graduation Method

  Copyright (c) 2013, rhambach.
    This file is part of the TEMareels package and released
    under the MIT-Licence. See LICENCE file for details.
"""

from scipy.misc import comb
from scipy import sparse
from scipy.sparse.linalg import spsolve
import numpy as np

def whfilter(a, weights=None, lamb=1600, p=3, ):
    """
    Generalized Whittaker-Handerson Graduation Method

    Parameters
    ----------
    a : array-like
          The input array, shape (n,)
    weights : array-like or None
          Weights
    lamb : float
          The relative importance between goodness of fit
          and smoothness (smoothness increases with lamb).
    p : integer, default 3
          The degree of smoothness. We minimize the p-th
          finite-differences of the graduated data. Examples:
          p=2 Hodrick-Prescott filter;
          p=3 Whittaker-Henderson method;
          Note: moments 0..p-1 will be conserved by graduation

    Returns
    -------
    out : array
          The smoothed data

    References
    ----------
    implementation of scikits.statsmodels.tsa.filters.hp_filter.py
    Alicja S. Nocon & William F. Scott (2012): "An extension of the
       Whittaker-Henderson method of graduation", Scandinavian
       Actuarial Journal, 2012:1, 70-79
    Whittaker, E. T. (1922). "On a new method of graduation",
       Proceedings of the Edinburgh Mathematical Society 41,63-75.

    """
    # input data
    a = np.squeeze(a);
    if a.ndim>1: raise ValueError("input array a must be 1d");
    n = a.size;

    # weights
    W = np.squeeze(weights) if weights is not None else np.ones(n);
    if np.any(W==0) or not np.all(np.isfinite(W)):
      raise ValueError("weights must be non-zero and finite.");
    W = sparse.dia_matrix((W, 0), shape=(n,n));

    # set up difference Matrix K, shape (n-p, n)
    # K_ij = k(j-i),  l=j-i
    # k(l) = (-1)^l Binomial(p,l) if 0<=l<=p else 0
    l = np.arange(p+1);
    k = (-1)**l * comb(p,l);       # same as K_0j
    diags  =np.tile(k,(n,1)).T;    # side-diagonal K_i,i+l; n-times k(l)
    offsets=np.arange(p+1);        # index of side-diagonals
    K = sparse.dia_matrix((diags,offsets),shape=(n-p,n)); # K_ij

    # solve quadratic optimization problem
    return spsolve(W+lamb*K.T.dot(K), W.dot(a));

# -----------------------------------------------------------------
if __name__ == "__main__":
  import matplotlib.pylab as plt

  x = np.arange(200);
  y = x**2 + np.random.normal(0,1000,200);

  yp= whfilter(y,lamb=0.01,weights=1./(1+np.abs(y)));
  plt.plot(x,y,label='original data');
  plt.plot(x,yp,label='smoothed data');
  plt.plot(x,y-yp,label='error');

  plt.show();


from tabulate import tabulate
import pandas as pd
from scipy.integrate import odeint
import numpy as np
import matplotlib.pyplot as plt
from string import Template
from math import e

# Constants
n = 5
a = b = 1 + 0.4 * n
interval = [0, 4]
h = 0.1
x0 = y0 = 0
epsilon = 10 ** (-1)
template = Template('#' * 10 + ' $string ' + '#' * 10)

def dfunction(y: float, x: float) -> float:
    """
    Main diff function
    :param x: x-argument
    :param y: y-argument
    :return: result
    """
    return e ** (-a * x) * (y ** 2 + b)

def system_function(y: list, x: float) -> list:
    """
    Main diff system
    :param y: y-argument
    :param x: x-argument
    :return: list of results, tow arguments
    """
    k = 10
    return [y[1], ((k - 10) / 10 * y[1]) - y[0]]

def show_plot(x_axis_1: list, y_axis_1: list, x_axis_2: list, y_axis_2: list, labels: list) -> None:
    """
    Function for showing plot
    :param x_axis_1: x-values for the first function
    :param y_axis_1: y-values for the first function
    :param x_axis_2: x-values for the second function
    :param y_axis_2: y-values for the second function
    :param labels: labels on a plot
    :return: nothing to return
    """
    fig, ax = plt.subplots()
    ax.plot(x_axis_1, y_axis_1, label=labels[0])
    ax.plot(x_axis_2, y_axis_2, label=labels[1])
    ax.legend(loc='upper left', ncol=2)
    plt.grid()
    plt.show()

def show_plot_for_system(x_axis: list, y_axis: list, labels: list) -> None:
    """
    Function for showing plots for system
    :param x_axis: x-values of the function
    :param y_axis: y-values of the function
    :param labels: labels on a plot
    :return: nothing to return
    """
    plt.title(labels[2])
    plt.xlabel(labels[0])
    plt.ylabel(labels[1])
    plt.grid()
    plt.plot(x_axis, y_axis, 'k')
    plt.show()

def runge_kutte_method(limits: list, h_value: float, epsilon_value: float, first_x: float, first_y: float) -> list:
    """
    Implementation of the Runge-Kutta method
    :param limits: limits of x-values
    :param h_value: step
    :param epsilon_value: value for controlling the fault
    :param first_x: known x-value
    :param first_y: known y-value
    :return: list with x and y values
    """
    results = []
    results.append([first_x, first_y, 0])
    current_x = first_x
    current_y = first_y
    while current_x < limits[1]:
        k1 = h_value * dfunction(current_y, current_x)
        k2 = h_value * dfunction(current_y + k1 / 2, current_x + h_value / 2)
        k3 = h_value * dfunction(current_y + k2 / 2, current_x + h_value / 2)
        k4 = h_value * dfunction(current_y + k3, current_x + h_value)
        delta_y = (1.0 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4)
        current_x += h_value
        current_y += delta_y
        fault = abs((k2 - k3) / (k1 - k2))
        if fault > epsilon_value:
            h_value /= 2
        results.append([current_x, current_y])
    return results

def adams_method(limits: list, h_value: float, epsilon_value: float, runge_kutta_results: list) -> list:
    """
    Implementation of the Adams method
    :param limits: limits of x-values
    :param h_value: step
    :param epsilon_value: alue for controlling the fault
    :param runge_kutta_results: known results from the previous method
    :return: list with x and y values
    """
    index = 3
    step_value = h_value
    number_of_steps = ((limits[1] - limits[0]) / h_value)
    while index < number_of_steps:
        k1 = dfunction(runge_kutta_results[index][1], runge_kutta_results[index][0])
        k2 = dfunction(runge_kutta_results[index - 1][1], runge_kutta_results[index - 1][0])
        k3 = dfunction(runge_kutta_results[index - 2][1], runge_kutta_results[index - 2][0])
        k4 = dfunction(runge_kutta_results[index - 3][1], runge_kutta_results[index - 3][0])
        extra_y = runge_kutta_results[index][1] + h_value / 24 * (55 * k1 - 59 * k2 + 37 * k3 - 9 * k4)
        next_x = runge_kutta_results[index][0] + step_value
        intra_y = runge_kutta_results[index][1] + h_value / 24 * (9 * dfunction(extra_y, next_x) + 19 * k1 - 5 * k2 + k3)
        fault = abs(intra_y - extra_y)
        if fault > epsilon_value:
            step_value / 2
        if extra_y == intra_y:
            runge_kutta_results.append([next_x, extra_y])
        else:
            runge_kutta_results.append([next_x, intra_y])
        index += 1
    return runge_kutta_results

def search_error_for_runge_kutta(runge_kutta_results: list, h_value: float) -> list:
    """
    Function for calculating errors for Runge-Kutta method
    :param runge_kutta_results: results from this method
    :param h_value: step
    :return: list with errors
    """
    errors = []
    for index in range(len(runge_kutta_results) - 1):
        k1 = dfunction(runge_kutta_results[index][1], runge_kutta_results[index][0])
        k2 = dfunction(runge_kutta_results[index][1], runge_kutta_results[index][0] + h_value / 2)
        k3 = dfunction(runge_kutta_results[index][1], runge_kutta_results[index][0] + h_value / 2)
        k4 = dfunction(runge_kutta_results[index][1], runge_kutta_results[index][0] + h_value)
        delta_y = (k1 + 2 * k2 + 2 * k3 + k4) / 6
        right_part = (runge_kutta_results[index + 1][1] - runge_kutta_results[index][1]) / h_value
        error = delta_y - right_part
        errors.append(error)
    return errors

def search_error_for_adams(adams_results: list, adams_results_less: list, m: int) -> list:
    """
    Function for calculating errors for Adams method
    :param adams_results: results from this method
    :param adams_results_less: results from this method with divided step
    :param m: value for the fault
    :return: list with errors
    """
    errors = []
    for index in range(len(adams_results)):
        error = (adams_results[index][1] - adams_results_less[index * 2][1]) / (2 ** m - 1)
        errors.append(error)
    return errors

def print_table(table: list, headers: tuple) -> None:
    """
    Function for printing the table
    :param table: values
    :param headers: headers of the table
    :return: nothing
    """
    dataframe = pd.DataFrame(table)
    format_style = 'github'
    print(tabulate(dataframe, headers=headers, tablefmt=format_style))

def scipy_solver(x_axis: list, first_y: float) -> list:
    """
    Solve the diff equation with SciPy
    :param x_axis: x-values
    :param first_y: known y-value
    :return: list with results
    """
    results = odeint(dfunction, first_y, x_axis)
    return results

def system_solver():
    y_axis = [0.1, 0]
    x_axis = np.linspace(0, 40, 100)
    results = odeint(system_function, y_axis, x_axis)
    first_y_results = results.transpose()[0]
    second_y_results = results.transpose()[1]
    show_plot_for_system(x_axis, first_y_results, ['u<0>', 'u<1>', 'First function'])
    show_plot_for_system(x_axis, second_y_results, ['u<0>', 'u<2>', 'Second function'])
    show_plot_for_system(first_y_results, second_y_results, ['u<1>', 'u<2>', 'Portrait'])

print(template.substitute(string='Runge-kutta method'))
runge_kutta_results = runge_kutte_method(interval, h, epsilon, x0, y0)
runge_kutta_results_less = runge_kutte_method(interval, h / 2, epsilon, x0, y0)
runge_kutta_errors = search_error_for_adams(runge_kutta_results, runge_kutta_results_less, 5)
for index in range(1, len(runge_kutta_errors)):
    runge_kutta_results[index].append(abs(runge_kutta_errors[index]))
print_table(runge_kutta_results, ('x', 'y', 'Error'))
print(template.substitute(string='Adams method'))
adams_results = adams_method(interval, h, epsilon, runge_kutta_results[:4])
adams_results_less = adams_method(interval, h / 2, epsilon, runge_kutta_results_less[:4])
adams_errors = search_error_for_adams(adams_results, adams_results_less, 4)
for index in range(len(adams_errors)):
    if index < 4:
        adams_results[index][2] = abs(adams_errors[index])
    else:
        adams_results[index].append(abs(adams_errors[index]))
print_table(adams_results, ('x', 'y', 'Error'))
x_axis = np.arange(interval[0], interval[1] + 0.1, h)
scipy_results = scipy_solver(x_axis, y0)
print(template.substitute(string='SciPy solution'))
print_table([[x_axis[i], scipy_results[i]] for i in range(len(scipy_results))], ('x', 'y'))
show_plot([el[0] for el in runge_kutta_results], [el[1] for el in runge_kutta_results], [el[0] for el in adams_results], [el[1] for el in adams_results], ['Runge-Kutta', 'Adams'])
show_plot([el[0] for el in runge_kutta_results], [el[2] for el in runge_kutta_results], [el[0] for el in adams_results], [el[2] for el in adams_results], ['Runge-Kutta errors', 'Adams errors'])
system_solver()


import numpy as np

class PCA:

    def __init__(self, n_components):
        self.n_components = n_components
        self.components = None
        self.mean = None

    def fit(self, X):

        # Calculate the mean of `X`
        self.mean = np.mean(X, axis=0)

        # Centering of Mean for variance
        X = X - self.mean

        # Calculate the Covariances from given `X` functions needs samples as columns
        cov = np.cov(X.T)

        # Calculate the eigenvalues and eigenvectors
        eigenvalues, eigenvectors = np.linalg.eig(cov)

        # Sort the eigenvectors in descending order
        eigenvectors = eigenvectors.T
        idxs = np.argsort(eigenvalues)[::-1]
        eigenvalues = eigenvalues[idxs]
        eigenvectors = eigenvectors[idxs]

        # Store the first n eigenvectors inside `self.components`
        self.components = eigenvectors[0:self.n_components]

    def transform(self, X):
        X = X - self.mean
        return np.dot(X, self.components.T)


import numpy as np

def cosine_sim (v1: np.ndarray, v2: np.ndarray) -> float:
	""" Cosine similarity between two vectors."""
	return (np.dot (v1, v2)/(np.linalg.norm (v1, 2) * np.linalg.norm (v2, 2)))

def cosine_dist (v1: np.ndarray, v2:np.ndarray) -> float:
	""" Cosine distance between two vectors."""
	return 1 - cosine_sim(v1, v2)

def neighbors(word:str, embs:np.array, voc:tuple, k=3) -> list:
	""" Get the list of near neighbors for a given word from the embeddings.

		Each row of the matrix `embs` is a vector for a word.
		The mapping of words and row numbers is in `voc`.
	"""
	w2i, i2w = voc
	vec_len = np.linalg.norm (embs[w2i[word], :], 2)
	norms = np.linalg.norm (embs, 2, axis=1)

	sims = np.dot(embs[w2i[word],], embs.T)
	sims = sims / (vec_len * norms)

	output = []
	for sim_idx in sims.argsort()[::-1][1:(1+k)]:
		if sims[sim_idx] > 0:
			output.append(i2w[sim_idx])
	return output

def get_neighbor_sims(word:str, neighbors_set:set, vec: np.ndarray, voc:tuple) -> np.array:
	w2i, i2w = voc
	v_self = vec[w2i[word], :]
	v_neighbors = vec[[w2i[neighbor] for neighbor in neighbors_set], :]

	vec_len = np.linalg.norm (v_self, 2)
	norms = np.linalg.norm (v_neighbors, 2, axis=1)

	sims = np.dot (v_self, v_neighbors.T)
	sims = sims / (vec_len * norms)

	return sims

def hamilton_local_score (word:str, voc:tuple, old:np.array, new:np.array, k=50) -> float:
	near_neighbors_old = neighbors (word, old, voc, k=k)
	near_neighbors_new = neighbors (word, new, voc, k=k)
	common_neighbors = set (near_neighbors_old).union (near_neighbors_new)

	sim_old = get_neighbor_sims (word, common_neighbors, old, voc)
	sim_new = get_neighbor_sims (word, common_neighbors, new, voc)

	return cosine_dist (sim_old, sim_new)

def hamilton_global_score (word:str, voc:tuple, old:np.ndarray, new: np.ndarray) -> float:
	w2i, i2w = voc
	return cosine_dist (old[w2i[word],:], new[w2i[word], :])


# Linear regression on one-dimensional data using a closed form solution.
# Please make sure matplotlib is included in the conda_dependencies.yml file.

import numpy as np
import matplotlib
matplotlib.use('agg')
import matplotlib.pyplot as plot
fig = plot.figure()

# load data
X = []
Y = []
for line in open('data.csv'):
    x, y = line.split(',')
    X.append(float(x))
    Y.append(float(y))

# turn them into numpy arrays so we can apply matrix operations
X = np.array(X)
Y = np.array(Y)

# this is the common denominator
denominator = X.dot(X) - X.mean() * X.sum()

# value of a
a = (X.dot(Y) - Y.mean() * X.sum()) / denominator

# value of b
b = (Y.mean() * X.dot(X) - X.mean() * X.dot(Y)) / denominator

# Yhat is simply aX + b
Yhat = a * X + b
print ("Coefficient: {0}, intercept: {1}".format(a, b))

# Plot the data and the fitted line, then save it into a png file in the output directory.
ax = fig.gca()
ax.scatter(X, Y)
ax.plot(X, Yhat, color='magenta')
fig.savefig('./outputs/lin.png')

### compute r-squared ###
# residual error of the prediction
d1 = Y - Yhat
# intrinsic error to mean
d2 = Y - Y.mean()

# if r2 is 1 (i.e., d1 is 0), this is a perfect model with no errors.
# if r2 is 0 (i.e., d1 is the same as d2), this is a useless model as it is just the same as predicting mean.
# if r2 is less than 0 (i.e., d1 is larger that d2), you are doing worse than predicting mean!!
r2 = 1 - d1.dot(d1) / d2.dot(d2)
print ("R-squared: {}.".format(r2))


from sympy import symbols, solve, pprint

x = Symbol( 'x' )

expr = x - 5 - 7

solve( expr )

expr = x**2 + 5*x + 4
solve( expr )

s, u, t, a = symbols( 's, u, t, a' )

expr = u * t + ( 1 / 2 ) * a * t * t - s

t_expr = solve( expr, t, dict = True )

pprint( t_expr )

x, a, b, c = symbols( 'x, a, b, c' )
expr = a*x*x + b*x + c
sln = solve( expr, x, dict = True )

pprint( sln )

# System of Equations

x = Symbol( 'x' )
y = Symbol( 'y' )

expr1 = 2*x + 3*y - 6
expr2 = 3*x + 2*y - 12

soln = solve( ( expr1, expr2 ), dict = True )

soln = soln[ 0 ]

expr1.subs( {x:soln[ x ], y: soln[ y ] } )
expr2.subs( {x:soln[ x ], y: soln[ y ] } )


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

# @uthor: Makram Jandar
#     ___     _ ______ ____ __ __   ___ ______ ____ __   ___
#   /    |  /  ]      |    |  |  |/    |      |    /   \|    \
#  |  o  | /  /|      ||  ||  |  |  o  |      ||  |     |  _  |
#  |     |/  / |_|  |_||  ||  |  |     |_|  |_||  |  O  |  |  |
#  |  _  /   \_  |  |  |  ||  :  |  _  | |  |  |  |     |  |  |
#  |  |  \     | |  |  |  | \   /|  |  | |  |  |  |     |  |  |
#  |__|__|\____| |__| |____| \_/ |__|__| |__| |____\___/|__|__|
#                  © Activation and derivatives Functions Class

from numpy import (exp, array, maximum, square, log, tanh, arctan, ones_like,
                   where, clip)

""" Several Activation and derivatives Functions """
class Activation():
    def __init__(self, function, derivative, Z, dA):
        self.function = function
        self.derivative = derivative
        self.Z = Z
        self.dA = dA

    ''' Computing activations and derivatives '''
    def compute(self):

        # Sigmoid function and derivative
        if self.function == 'Sigmoid':
            dZ = 1 / (1 + exp(-self.Z))
            return self.dA * (dZ * (1 - dZ)) if self.derivative else dZ

        # ReLU function and derivative
        elif self.function == 'ReLU':
            if self.derivative:
                dZ = array(self.dA, copy=True)
                dZ[self.Z <= 0] = 0
                return dZ
            return maximum(0, self.Z)

        # TanH function and derivative
        elif self.function == 'TanH':
            return 1 - square(self.Z) if self.derivative else tanh(self.Z)

        # ArcTan function and derivative
        elif self.function == 'ArcTan':
            return 1 / (1 + square(self.Z)) if self.derivative else arctan(self.Z)

        # Identity function and derivative
        elif self.function == 'Identity':
            return ones_like(self.Z) if self.derivative else self.Z

        # ELU function and derivative
        elif self.function == 'ELU':
            if self.derivative:
                return where(self.Z < 0, alpha * (exp(self.Z)), 1)
            return where(self.Z < 0, alpha * (exp(self.Z) - 1), self.Z)

        # LeakyReLU function and derivative
        elif self.function == 'LeakyReLU':
            if self.derivative:
                return where(self.Z > 0, self.Z, self.Z * 0.01)
            return clip(self.Z > 0, 0.01, 1.0)

        # SoftPlus function and derivative
        elif self.function == 'SoftPlus':
            if self.derivative:
                return 1 / (1 + exp(-array(self.Z)))
            return log(1 + exp(self.Z))


import numpy as np

def solve_QR(Q, R, y):
    #Solve QR x = y

    w = np.matmul(Q.T, y)

    x = np.zeros((R.shape[1]))

    for i in range(1, x.size+1):
        x[-i] = w[-i]
        for j in range(1,i):
            x[-i] -= R[-i, -j]*x[-j]

        x[-i]/=R[-i, -i]

    return x

if __name__=="__main__":
    from decompose import factor_QR, rotation_QR
    A = np.array([[-1, -2, 1], [-1, -3, -2], [2,1,-3]])
    Q, R = factor_QR(A)
    print(np.linalg.solve(A, np.array([0,0,0])))
    print(solve_QR(Q, R, np.array([0,0,0])))


#Consider a 2-by-2 identity matrix, I. We see that this is a basis of ℜ2 spanned by vectors [1,0] and [0,1]. Solve the unique solution for b = [3,4] - call this sol1.

#Given matix A, compute the determinant - call this det1. Find the inverse of this matrix - call this mat1. We see that this is a basis of ℜdim. What integer is dim? This is spanned by two vectors, one being [0,1]. What is the other one - call this bas1 Solve the unique solution for b = [3,4] - call this sol2. (Observe how matrix A and the identity matrix give two different solutions.)
#Given matix B, compute the determinant - call this det2. We see that matrix is noninvertible and the rows are linearly dependent. Infact we see this matrix is reducible if we perform the following operation on row 2: alpha x row 1 - row 2. What is the value of alpha?

import numpy as np

A=[[1,1],[0,1]]
B =[[1,2],[3,6]]
I=[[1,0],[0,1]]
b=[3,4]
bas1=[1,1]
sol1= np.linalg.solve(I,b)
det1=np.linalg.det(A)
mat1=np.linalg.inv(A)
sol2=np.linalg.solve(A,b)
det2=np.linalg.det(B)


import math
from sympy import Symbol, Limit, Derivative, solve, sympify, S, simplify, lambdify, pprint, init_printing

init_printing( order = 'rev-lex', use_unicode = True )

# 3.2.1

# A particle moves along a straight line with its position give by the function:
# p = f(t) = t^2 -2t +4,
# where p is given in meters and t in seconds.
# When does the velocity equal zero?

Pt = t**2 - t*2 + 4
t = Symbol( 't' )
d = Derivative( Pt, t ).doit()
sln = solve( d, t, dict = True )
pprint( sln[ 0 ] )

# 3.2.1

# Suppose f(X) = x^2 - 3x
# What is the equation of the line tangent to the curve at the point ( 2, -2 ) ?

# 3.2.4

# A particle moves along a straight line with its position given by the function
# p = f(t) = 3t^2 - 2t + 4, where p is given in meters and t in seconds.
# For what value of t does the velocity of the particle equal 10 MPS?

Pt = 3*t**2 - t*2 + 4
t = Symbol( 't' )
d = Derivative( Pt, t ).doit()

# Solve for t = 10
Ds = d - 10
sln = solve( Ds, t, dict = True )
pprint( sln[ 0 ] )

# 3.2.5

# At what point is the slope of the line tangent to curve
# y = x^2 - 2x + 1 equal to 2?

Fx = x**2 - 2*x + 1
x = Symbol( 'x' )
d = Derivative( Fx, x ).doit()

# solve for x = 2
Ds = d - 2
px = solve( Ds, x )[ 0 ]
py = Fx.subs( { x: px } )

pprint( '( {0}, {1} )'.format( px, py ) )

# 3.2.6

# A child kicks a soccer ball across a field. The distance between the soccer ball and
# the child in feet is given by the formula:
# p(t) = 25t - t^2, 0 <= t < 25, where t is measured in seconds.
# How fast is the soccer ball moving when t = 5 seconds?

Pt = 25*t - t**2
t = Symbol( 't', positive = True )
d = Derivative( Pt, t ).doit()
d.subs( { t: 5 } )

# 3.2.7

# The rate of production at ACME toys can be determined from the production formula P(t) = 1600t - 100t^2,
# where P(t) is the number of units produced in a given time and t is the time in hours since the factory shift
# began. At what time does the rate of toy production stop?

Pt = 1600*t - 100*t**2
t = Symbol( 't' )
d = Derivative( Pt, t ).doit()
solve( d, t )[ 0 ]

# 3.2.8

# A student goes crabbing after math class. He drops the crab cage, and waits. Let f(t) denote the distance
# a crab is from the cage at any time. Assume f(t) = -2t^2 - 7t + 15, where t is measured in hours, and
# f(t) is in feet. How long does the student need to wait before the crab is in the cage?

Ft = -2*t**2 - 7*t + 15
t = Symbol( 't', positive = True )
solve( Ft, t )

# 3.2.10

# Consider the function f(x) = x^2 - x. Using the fact that f'(x) = 2x -1, find the point (x,y) on the
# graph of f(x) where the tangent line is a horizontal line.

Fx = x**2 - x
FPx = 2*x -1
x = Symbol( 'x', positive = True )

# Tangent line is horizontal where f'(x) = 0
px = solve( FPx, x )[ 0 ]
# y value = f( f'( 0 ) )
py = Fx.subs( { x: px } )

pprint( '( {0}, {1} )'.format( px, py ) )


import numpy as np

class LossAndDerivatives:
    @staticmethod
    def mse(X, Y, w):
        """
        X : numpy array of shape (`n_observations`, `n_features`)
        Y : numpy array of shape (`n_observations`, `target_dimentionality`) or (`n_observations`,)
        w : numpy array of shape (`n_features`, `target_dimentionality`) or (`n_features`,)

        Return : float
            single number with MSE value of linear model (X.dot(w)) with no bias term
            on the selected dataset.

        Comment: If Y is two-dimentional, average the error over both dimentions.
        """

        return np.mean((X.dot(w) - Y)**2)

    @staticmethod
    def mae(X, Y, w):
        """
        X : numpy array of shape (`n_observations`, `n_features`)
        Y : numpy array of shape (`n_observations`, `target_dimentionality`) or (`n_observations`,)
        w : numpy array of shape (`n_features`, `target_dimentionality`) or (`n_features`,)

        Return: float
            single number with MAE value of linear model (X.dot(w)) with no bias term
            on the selected dataset.

        Comment: If Y is two-dimentional, average the error over both dimentions.
        """

        # YOUR CODE HERE
        return

    @staticmethod
    def l2_reg(w):
        """
        w : numpy array of shape (`n_features`, `target_dimentionality`) or (`n_features`,)

        Return: float
            single number with sum of squared elements of the weight matrix ( \sum_{ij} w_{ij}^2 )

        Computes the L2 regularization term for the weight matrix w.
        """

        # YOUR CODE HERE
        return

    @staticmethod
    def l1_reg(w):
        """
        w : numpy array of shape (`n_features`, `target_dimentionality`)

        Return : float
            single number with sum of the absolute values of the weight matrix ( \sum_{ij} |w_{ij}| )

        Computes the L1 regularization term for the weight matrix w.
        """

        # YOUR CODE HERE
        return

    @staticmethod
    def no_reg(w):
        """
        Simply ignores the regularization
        """
        return 0.

    @staticmethod
    def mse_derivative(X, Y, w):
        """
        X : numpy array of shape (`n_observations`, `n_features`)
        Y : numpy array of shape (`n_observations`, `target_dimentionality`) or (`n_observations`,)
        w : numpy array of shape (`n_features`, `target_dimentionality`) or (`n_features`,)

        Return : numpy array of same shape as `w`

        Computes the MSE derivative for linear regression (X.dot(w)) with no bias term
        w.r.t. w weight matrix.

        Please mention, that in case `target_dimentionality` > 1 the error is averaged along this
        dimension as well, so you need to consider that fact in derivative implementation.
        """

        # YOUR CODE HERE
        return

    @staticmethod
    def mae_derivative(X, Y, w):
        """
        X : numpy array of shape (`n_observations`, `n_features`)
        Y : numpy array of shape (`n_observations`, `target_dimentionality`) or (`n_observations`,)
        w : numpy array of shape (`n_features`, `target_dimentionality`) or (`n_features`,)

        Return : numpy array of same shape as `w`

        Computes the MAE derivative for linear regression (X.dot(w)) with no bias term
        w.r.t. w weight matrix.

        Please mention, that in case `target_dimentionality` > 1 the error is averaged along this
        dimension as well, so you need to consider that fact in derivative implementation.
        """

        # YOUR CODE HERE
        return

    @staticmethod
    def l2_reg_derivative(w):
        """
        w : numpy array of shape (`n_features`, `target_dimentionality`) or (`n_features`,)

        Return : numpy array of same shape as `w`

        Computes the L2 regularization term derivative w.r.t. the weight matrix w.
        """

        # YOUR CODE HERE
        return

    @staticmethod
    def l1_reg_derivative(w):
        """
        Y : numpy array of shape (`n_observations`, `target_dimentionality`) or (`n_observations`,)
        w : numpy array of shape (`n_features`, `target_dimentionality`) or (`n_features`,)

        Return : numpy array of same shape as `w`

        Computes the L1 regularization term derivative w.r.t. the weight matrix w.
        """

        # YOUR CODE HERE
        return

    @staticmethod
    def no_reg_derivative(w):
        """
        Simply ignores the derivative
        """
        return np.zeros_like(w)


# -*- coding: utf-8 -*-
"""class 4 problem 1 offline

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1b1FlV6lo-nDxUESU_e-QBtzz-1yE-AJg
"""

# libraries
import random
import math
import numpy as np
import matplotlib.pyplot as plt
random.seed(10)

def monte_carlo(N):

  A = 0
  B = 2
  y_values = []
  y_square_values =[]
  for i in range(1,N+1):
    # x random value generation
    x = random.uniform(A,B)
    # calculating y
    y = (x**2) * math.exp(-x) * math.log(x+2)
    # store y and y^2 values in  lists
    y_values.append(y)
    y_square_values.append(y*y)

  # calculate avg valus for y and y^2
  y_average = sum(y_values) / len(y_values)
  y_square_average = sum(y_square_values) / len(y_square_values)

  # intregal area value
  area_by_integral = y_average * (B-A)
  print("For number of sample points: ", N)
  print("Integral Value: ",area_by_integral)

  # error estimate
  error = ( (B-A) / math.sqrt(N) ) * math.sqrt(y_square_average - y_average**2)
  print("Estimated Error: ", error)
  print("")
  return error

N =[100,1000,5000,10000]
est_error_list = []
for i in N:
  est_error_list.append(monte_carlo(i))
#print(est_error_list)
# bar plot
sample_points = ["100","1000","5000","10000"]
plt.bar(sample_points, est_error_list,color = "green")
plt.xlabel("Value of n (number of points)")
plt.ylabel("Error Estimate")
plt.show()

Assignment 03 - Monte Carlo Intregral/problem1.py


"""
  Name     : c14_24_cholesky.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

import numpy as np
# input area
nSimulation=5000              # number of simulations
c=np.array([[1.0, 0.5, 0.3],  # correlation matrix
            [0.5, 1.0, 0.4],
            [0.3, 0.4, 1.0]])
np.random.seed(123)           # fix random numbers
#
# generate uncorrelated random numbers
x=np.random.normal(size=3*nSimulation)
U=np.reshape(x,(nSimulation,3))
#
# Cholesky decomposition
L=np.linalg.cholesky(c)
# generate correlated random numbers
r=np.dot(U,L)
#check the correlation matrix
print(np.corrcoef(r.T))


#!/usr/bin/env python
"""
Vector operations
"""
import math
import numpy as np
# Local modules
import baldor as br

def unit(vector):
  """
  Return vector divided by Euclidean (L2) norm

  Parameters
  ----------
  vector: array_like
    The input vector

  Returns
  -------
  unit : array_like
    Vector divided by L2 norm

  Examples
  --------
  >>> import numpy as np
  >>> import baldor as br
  >>> v0 = np.random.random(3)
  >>> v1 = br.vector.unit(v0)
  >>> np.allclose(v1, v0 / np.linalg.norm(v0))
  True
  """
  v = np.asarray(vector).squeeze()
  return v / math.sqrt((v**2).sum())

def norm(vector):
  """
  Return vector Euclidaan (L2) norm

  Parameters
  ----------
  vector: array_like
    The input vector

  Returns
  -------
  norm: float
    The computed norm

  Examples
  --------
  >>> import numpy as np
  >>> import baldor as br
  >>> v = np.random.random(3)
  >>> n = br.vector.norm(v)
  >>> numpy.allclose(n, np.linalg.norm(v))
  True
  """
  return math.sqrt((np.asarray(vector)**2).sum())

def perpendicular(vector):
  """
  Find an arbitrary perpendicular vector

  Parameters
  ----------
  vector: array_like
    The input vector

  Returns
  -------
  result: array_like
    The perpendicular vector
  """
  if np.allclose(vector, np.zeros(3)):
    # vector is [0, 0, 0]
    raise ValueError('Input vector cannot be a zero vector')
  u = unit(vector)
  if np.allclose(u[:2], np.zeros(2)):
    return br.Y_AXIS
  result = np.array([-u[1], u[0], 0], dtype=np.float64)
  return result

def skew(vector):
  """
  Returns the 3x3 skew matrix of the input vector.

  The skew matrix is a square matrix `R` whose transpose is also its negative;
  that is, it satisfies the condition :math:`-R = R^T`.

  Parameters
  ----------
  vector: array_like
    The input array

  Returns
  -------
  R: array_like
    The resulting 3x3 skew matrix
  """
  skv = np.roll(np.roll(np.diag(np.asarray(vector).flatten()), 1, 1), -1, 0)
  return (skv - skv.T)

def transform_between_vectors(vector_a, vector_b):
  """
  Compute the transformation that aligns two vectors

  Parameters
  ----------
  vector_a: array_like
    The initial vector
  vector_b: array_like
    The goal vector

  Returns
  -------
  transform: array_like
    The transformation between `vector_a` a `vector_b`
  """
  newaxis = unit(vector_b)
  oldaxis = unit(vector_a)
  # Limits the value of `c` to be within the range C{[-1, 1]}
  c = np.clip(np.dot(oldaxis, newaxis), -1., 1.)
  angle = np.arccos(c)
  if np.isclose(c, -1.0) or np.allclose(newaxis, oldaxis):
    axis = perpendicular(newaxis)
  else:
    axis = unit(np.cross(oldaxis, newaxis))
  transform = br.axis_angle.to_transform(axis, angle)
  return transform


#!/usr/bin/env python3
# Viterbi Ave minimization problem
# based on skeleton code by D. Crandall, March 2019

import numpy as np

# problem definition
home_elevations = [10, 12, 15, 1, 25, 0, 1, 16, 16, 18]
N = 10 # cost of raising/lowering a home 1 foot
M = 1  # constact factor in cost of staircases

possible_elevations = range(0, 26)
home_count = len(home_elevations)

# left_messages[i] is the message *to* the i-th home, from the i+1-th home
# right_messages[i] is the message to the i-th home, from the i-1-th home
left_messages = np.zeros((home_count, 26),)
right_messages = np.zeros((home_count, 26),)

def D(my_elevation, i):
    return N*abs(home_elevations[i]-my_elevation) ## done

def V(my_elevation, neighbor_elevation):
    return M*(my_elevation-neighbor_elevation)*(my_elevation-neighbor_elevation) ## done

for iteration in range(0, home_count*2):
    new_left_messages = np.zeros((home_count, 26),)
    new_right_messages = np.zeros((home_count, 26),)

    for i in range(0, home_count-1):
        for neighbor_elevation in possible_elevations:
            new_right_messages[i+1][neighbor_elevation] = np.min([(D(x,i)+V(x,neighbor_elevation)+right_messages[i][x]) for x in possible_elevations])  ## done

    for i in range(1, home_count):
        for neighbor_elevation in possible_elevations:
            new_left_messages[i-1][neighbor_elevation] =  np.min([(D(x,i)+V(x,neighbor_elevation)+left_messages[i][x]) for x in possible_elevations])  ## done

    np.copyto(left_messages, new_left_messages)
    np.copyto(right_messages, new_right_messages)

# finally, every home chooses its best elevation based on the last set of neighbors and its own D() cost
new_elevations = [0] * home_count
for i in range(0, len(home_elevations)):
    new_elevations[i] = np.argmin([(right_messages[i][my_elevation] + left_messages[i][my_elevation] + D(my_elevation, i)) for my_elevation in possible_elevations ] )

# calculate the cost of the final answer
cost = (new_elevations[0] - home_elevations[0])*N
for i in range(1, len(home_elevations)):
    cost += abs(new_elevations[i] - home_elevations[i])*N + M*(new_elevations[i] - new_elevations[i-1])**2

print("Problem inputs:")
print("   Cost of raising/lowering yard: $%d/foot" % N)
print("   Cost of staircase            : height squared x $%d" % M)
print("   Current yard elevations      : " + str(home_elevations))
print("")
print("Solution:")
print("   Min cost to install sidewalk : $%d" % cost)
print("   New yard elevations          : " + str(new_elevations))


#%%
import numpy as np
import scipy as sp
from scipy import linalg
from scipy import optimize
from scipy import interpolate
import sympy as sm
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
#%%
sm.init_printing(use_unicode=True)

#%%
q1 = sm.symbols('q_1')
q2 = sm.symbols('q_2')
p1 = sm.symbols('p_1')
p2 = sm.symbols('p_2')
rho = sm.symbols('rho')
mu = sm.symbols('mu')
I = sm.symbols('I')
U= sm.symbols('U')

#%%
utility=(q1**rho+q2**rho)**(1/rho)
utility

#%%
utility=sm.Eq(log(q1)**rho*q2**mu,U)
utility
#%%
budgcons=sm.Eq(p1*q1+p2*q2,I)
budgcons
#%%
test= sm.Eq((q2**2/(q1+q2)**2)*p1-(q1**2/(q1+q2)**2)*p2,0)
test
#%%
testsolveq2=sm.solve(test,q2)
testsolveq2
#%%
testsolveq1=sm.solve(test,q1)
testsolveq1
#%%
anden=sm.Eq(q1*p1+q1*sm.sqrt(p2/p1)*p2,I)
anden
#%%
andensolve=sm.solve(anden,q1)
anden
#%%
testsubs=budgcons.subs(q2,testsolveq2[0])
testsubs
#%%
indiff=sm.solve(utility,q2)
indiff
#%%
q2_iso=sm.solve(budgcons,q2)
q2_iso
#%%
util_subs=utility.subs(q2,q2_iso[0])
util_subs
#%%
def find_opt(p1,p2,I,rho,mu):
#%%
foc=sm.diff(util_subs,q1)
foc
#%%
sol = sm.solve(sm.Eq(foc,0),q1)
sol
#%%
rho = 0.8
mu = 0.2
p1, p2 = 0.5, 1
I = 100

pmin, pmax = 0.5, 4
Imin, Imax = 10, 200
qmax = (3/4)*Imax/pmin

#%%
def consume_plot(p1=p1,p2=p2,I=I,rho=rho,mu=mu):

    q1 = np.linspace(0.1,qmax,num=100)
    q1e, q2e, uebar = sol(p1, p2 ,I, rho,mu)
    idfc = indiff(q1, uebar, rho, mu)
    budg = budgcons(q1, p1, p2, I)

    fig, ax = plt.subplots(figsize=(8,8))
    ax.plot(q1, budg, lw=2.5)
    ax.plot(q1, idfc, lw=2.5)
    ax.vlines(q1e,0,q2e, linestyles="dashed")
    ax.hlines(q2e,0,q1e, linestyles="dashed")
    ax.plot(q1e,q2e,'ob')
    ax.set_xlim(0, qmax)
    ax.set_ylim(0, qmax)
    ax.set_xlabel(r'$c_1$', fontsize=16)
    ax.set_ylabel('$c_2$', fontsize=16)
    ax.spines['right'].set_visible(False)
    ax.spines['top'].set_visible(False)
    ax.grid()
    plt.show()
#%%
consume_plot()
#%%
_sol_func = sm.lambdify((p1,I,rho,mu),sol[0])
def sol_func(p1,I=10,rho=1,mu=1):
    return _sol_func(p1,I,rho,mu)

# test
p1_vec = np.array([1.2,3,5,9])
demand_p1 = sol_func(p1_vec)
print(demand_p1)

import numpy,import scipy,from scipy,import sympy


"""
Numpy's mian object is the homogenoues multidimensional array.
It has an associated fast math function that operates on it.
It also provides simple routines for linear algebra and fft and
sophisticated random-number generation

Consider the numpy arrays like matrices with rows and columns
"""

import numpy as np #this is the most used way of importing numpy

#to can create a numpy array by using the array() keyword ,
#simillar to implicit conversion ie: int() ,str() in python

numpy_array = np.array([1,2,3,4,5])
#this is a multi dimentional array with single row

#to create multiple row and columns we have to use array( [ [ this is the element of the 1st row] ] ) *two sets of [ ] brackets
numpy_array_multidimensional = np.array([[1,2,4,5,5],[1,2,3,4,5],[5,10,14,20,25]])

#the shape of the array can be understood by the shape attribute

numpy_array_multidimensional.shape
#returs (3,5) three rows (three sets of data), with 5 columns (with 5 data in each set)

#there is a function transpose , which can be used to take the transpose of the array

numpy_array_multidimensional.transpose()

#arrage function returns a list of numbers from 0 --> n-1
#which is similar to range(n) and stores it in a list

random_number_list  = np.arange(25)

#reshape takes the single row list , generated by the arange function and
#turns it into m no.of lists with n no.of elements in each
#somewhat  like a matrix
random_number_list.reshape(5,3)

#this can also be done in single line as
single_line_random_number_list = np.arange(25).reshape(5,3)

#you can also use reshape with the previous array we generated

numpy_array_multidimensional =  numpy_array_multidimensional.reshape(5,3)

#we have some basic arithmatic operations which is used with matrices also
#like marix multiplication

multiplied_array = numpy_array_multidimensional.dot(random_number_list.reshape(5,3))

#remember two matrices can only be multipled when their order is
# m x n and n x p  so we reshaped the above matrix

#you can also add two arrays
array_one = np.arange(10).reshape(2,5)
array_two = np.array([43,5,2,84,8,1,6,74,32,74]).reshape(2,5)

array_three = array_one + array_two
#inorder to add two arrays their order must be same

#there are some trigonomertic functions available in numpy
array_one_sin = np.sin(array_one)
#this converts all the elements of the array_one into their respective sin values
array_two_cos = np.cos(array_two)
#simlar to the previous , this time into cos

#you can peform simple arithmatic operations on the array , which affects all the elements

array_one_half = array_one/2
array_one_power = array_one**2

#you know the all time simple trigo eqn sin(x)^2 + cos(x)^2 = 1
sum_of_sqrs = array_one_sin**2 + array_two_cos**2

#you can create an array with all zeros using
null_array_float = np.zeros(10)
#this will create a sinlge row array with all elements as zeroes,
#as default the array elements are of float type , inorder to chanege the datatype , we give the value to dtype
null_array_int = np.zeros(5, dtype=int)
identity_array_int = np.ones(5, dtype=int)
identity_array_int_shaped = np.ones((8,5), dtype=int)

#there are many more functions available within numpy like

array_one.sum()
#will return all the sums of the elements in an array

array_one.min()
#will will return the mininum value in the entire array , similarly there is .max() also


import numpy as np

def logistic(x):
    """
    Logistic function, mapping (-inf, inf) to (0,1)

    Parameters
    ----------
    x: float, int, array, list
        input variable

    Returns
    -------
    val: float, int, array
        logistic(x)
    """
    return np.exp(x)/(1+np.exp(x))

def logit(x, minval=0.001):
    """
    Logit function, mapping (0,1) to (-inf, inf)

    Parameters
    ----------
    x: float, int, array, list
        input variable
    minval: float (optional, default=0.001)
        minimum value of input x, and maximum of 1-x

    Returns
    -------
    val: float, int, array
        logit(x)
    """
    if isinstance(x,  (list, tuple, np.ndarray)):
        x[1-x<minval] = 1-minval
        x[x<minval] = minval
    else:
        x = max(minval, x)
        x = min(1-minval, x)
    val = np.log(x/(1-x))
    return val

def uninormal_pdf(x, mu, sigma, log=False):
    """
    Probability density function of univariate normal distribution

    Parameters
    ----------
    x: float, int, array, list
        input variable
    mu: float
        mean of the normal distribution
    sigma: float
        standard divation of the normal distibution
    log: bool (optional)
        If True, return pdf at log scale

    Returns
    -------
    pdf: float, int, array
        Probability density of x
    """
    mu = np.float(mu)
    sigma = np.float(sigma)
    pdf = np.log(1/(sigma*np.sqrt(2*np.pi))) - 0.5*((x-mu)/sigma)**2
    if log is False:
        pdf = np.exp(pdf)
    return pdf

def permutation_test(x1, x2, times=1000, sides=2, metrics="mean", seed=None):
    """
    Permutation test: whether group x1 has equal [mean|median] as group x2

    Parameters
    ----------
    x1: array or list of int/float
        samples for variable x1
    x2: array or list of int/float
        samples for variable x2
    times: int
        number of iterations, better <= 10^5
    sides: int, 1 or 2
        one-sided p value or two-sided p-value
    metrics: str
        the metrics: mean, median
    seed: int or None
        the seed for permutation

    Returns
    -------
    (deta, pval): float, float
        the difference between two groups and the p value

    Examples
    --------
    >>>x1 = np.random.normal(0,1,100)
    >>>x2 = np.random.normal(1,2,100)
    >>>permutation_test(x1, x2)
    """

    x1 = np.array(x1)
    x2 = np.array(x2)
    x1 = x1[x1 == x1]
    x2 = x2[x2 == x2]

    if metrics == "median":
        diff_obs = np.median(x1) - np.median(x2)
    else:
        diff_obs = np.mean(x1) - np.mean(x2)

    if seed is not None:
        np.random.seed(seed)

    cnt = 0
    xx = np.append(x1, x2)
    diff_all = np.zeros(times)
    for i in range(times):
        # np.random.shuffle(xx)
        xx = np.random.permutation(xx)
        if metrics == "median":
            diff_all[i] = np.median(xx[:len(x1)]) - np.median(xx[len(x1):])
        else:
            diff_all[i]  = np.mean(xx[:len(x1)]) - np.mean(xx[len(x1):])

    if sides == 2:
        pval = np.mean(np.abs(diff_all) >= np.abs(diff_obs))
    else:
        pval = np.mean(diff_all >= np.abs(diff_obs))

    return diff_obs, pval


import numpy as np
import theano.tensor as T

# Default activation functions
def sigmoid(x):
    """Sigmoid activation function

    :param x: Input value
    :type x: float or numpy.array

    :return: Sigmoid activated value: :math:`sigmoid(x) = \\dfrac{1}{1 + e^{-x}}`
    :rtype: float
    """
    return 1 / (1 + np.exp(-x))

def tanh(x):
    """Hyperbolic tangent activation function

    :param x: Input value
    :type x: float or numpy.array

    :return: Activated value: :math:`tanh(x)`
    :rtype: float
    """
    return np.tanh(x)

def relu(x):
    """Rectified Linear Units activation function

    :param x: Input value
    :type x: float or numpy.array

    :return: Activated value: :math:`relu(x) = \\max(0, x)`
    :rtype: float
    """
    return np.maximum(0, x)

def softplus(x):
    """Softplus activation function

    :param x: Input value
    :type x: float or numpy.array

    :return: Activated value: :math:`softplus(x) = \\log(1 + e^{-x})`
    :rtype: float
    """
    return np.log(1 + np.exp(-x))

def logit(x, weights, bias=0, tensor=False):
    """Logistic function

    :param x: Input value
    :type x: numpy.array
    :param weights: Vector of weights :math:`\\beta`
    :type weights: numpy.array
    :param bias: Vector of bias :math:`\\epsilon`, defaults to 0.
    :type bias: numpy.array
    :param tensor: Perform computation as tensor (theano type), defaults to False
    :type tensor: bool, optional

    :return: Logistic transformation: :math:`logit(x, \\beta) = \\dfrac{1}{1 + e^{-x \\beta}}`
    :rtype: float
    """

    if tensor:
        return 1 / (1 + T.exp(-T.dot(x, weights) + bias))
    else:
        return 1 / (1 + np.exp(-x.dot(weights) + bias))


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

#importing the scipy and numpy packages
from scipy import linalg
import numpy as np
from numpy.linalg import LinAlgError

#Declaring the numpy arrays
a = np.array([[3, 2, 0], [1, -1, 0], [0, 5, 1]])
b = np.array([2, 4, -1])

#Passing the values to the solve function
x = linalg.solve(a, b)

#printing the result array
print(x)

#Declaring the numpy array
A = np.array([[1,2],[3,4]])

#Passing the values to the det function
x = linalg.det(A)

#printing the result
print(x)

#Declaring the numpy array
A = np.array([[1,2],[3,4]])

#Passing the values to the eig function
l, v = linalg.eig(A)

#printing the result for eigen values
print(l)

#printing the result for eigen vectors
print(v)

#Declaring the numpy array
a = np.random.randn(3, 2) + 1.j*np.random.randn(3, 2)

#Passing the values to the eig function
U, s, Vh = linalg.svd(a)

# printing the result
print(U, Vh, s)

arr = np.array([[1, 2],
                [3, 4]])
print(linalg.det(arr))

arr = np.array([[3, 2],
                [6, 4]])
print(linalg.det(arr))

arr = np.array([[1, 2],
                [3, 4]])
iarr = linalg.inv(arr)
print(iarr)

np.allclose(np.dot(arr, iarr), np.eye(2))

arr = np.array([[3, 2],
                [6, 4]])
try:
    print(linalg.inv(arr))
except LinAlgError as lae:
    print(f"Error: {lae}")

arr = np.arange(9).reshape((3, 3)) + np.diag([1, 0, 1])
uarr, spec, vharr = linalg.svd(arr)
print(spec)
sarr = np.diag(spec)
svd_mat = uarr.dot(sarr).dot(vharr)
print(np.allclose(svd_mat, arr))


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

degree = 14
dataPoint = 60

if len(sys.argv)>1 :
	dataPoint = int(sys.argv[1])
print("dataPoint : {}".format( dataPoint ))

trainPoint = int(dataPoint * 3 / 4)
testPoint = int(dataPoint / 4)

one = np.ones(trainPoint)
noise = np.random.normal(size=dataPoint)
x = np.linspace(-3, 3, dataPoint)
x = np.random.permutation(x)
#print("{}".format( x ))

# training error and testing error

testX = x[trainPoint:]
X = np.vstack([ np.power(x[:trainPoint], degree), np.power(x[:trainPoint], degree-1) ])
for i in range(degree-2):
	X = np.vstack([ X, np.power(x[:trainPoint], degree-2-i) ])
X = np.vstack([ X, one])

#print("{}".format( noise ))
#print("{}".format( x ))
#print("{}".format( one ))
#print("{}".format( X ))
#print("{}".format( X.shape ))
#print("{}".format( testX ))

y = np.add(x[:trainPoint] * 2, noise[:trainPoint])
#print("{}".format( y ))

testY = np.add(x[trainPoint:] * 2, noise[trainPoint:])
#print("{}".format( testY ))

Wlin = np.linalg.inv(X.dot(X.T)).dot(X).dot(y.T)
#print("{}".format( Wlin ))

c = 0
for i in range(degree):
	c += np.power(x[:trainPoint], degree-i) * Wlin[i]
c += Wlin[degree]

d = 0
for i in range(degree):
	d += np.power(x[trainPoint:], degree-i) * Wlin[i]
d += Wlin[degree]

trainError = math.pow( np.square(np.subtract( c, y )).mean(), 1/2 )
print("training error : {}".format( trainError ))
testError = math.pow( np.square(np.subtract( d, testY )).mean(), 1/2 )
print("testing errors : {}".format( testError ))

a = np.linspace(-3, 3, 100)
b = 0
for i in range(degree):
	b += np.power(a, degree-i) * Wlin[i]
b += Wlin[degree]
#b = np.power(a, 5) * Wlin[0] + np.power(a, 4) * Wlin[1] + np.power(a, 3) * Wlin[2] + np.power(a, 2) * Wlin[3] + a * Wlin[4] + Wlin[5]

plt.scatter(x[:trainPoint], y, color = 'blue')
plt.scatter(x[trainPoint:], testY, color = 'yellow')
plt.plot(a, b, color = 'red')
plt.ylim(-8, 8)
plt.title('Fitting plots for training error and testing error\nwith degree - {D} and dataPoint {P}'.format( D=degree, P=dataPoint))
plt.xlabel("trainingPoint : blue , testingPoint : yellow")
plt.show()

# cross-validation errors ( five-fold )

sum = 0

for i in range(0, trainPoint, int(trainPoint/5)):

	deleted = []
	for j in range(int(trainPoint/5)):
		deleted.append(i+j)
	tmpX = np.delete(x[:trainPoint], deleted)
	validX = x[i:i+int(trainPoint/5)]
	X = np.vstack([ np.power(tmpX, degree), np.power(tmpX, degree-1) ])
	for j in range(degree-2):
		X = np.vstack([ X, np.power(tmpX, degree-2-j) ])
	cnt = trainPoint-int(trainPoint/5)
	X = np.vstack([ X, one[:cnt]])

	#print("{}".format( tmpX ))
	#print("{}".format( validX ))
	#print("{}".format( X ))

	tmpY = np.add(tmpX * 2, np.delete(noise[:trainPoint], deleted) )
	#print("{}".format( tmpY ))

	validY = np.add(validX * 2, noise[i:i+int(trainPoint/5)])
	#print("{}".format( validY ))

	Wlin = np.linalg.inv(X.dot(X.T)).dot(X).dot(tmpY.T)
	#print("{}".format( Wlin ))

	d = 0
	for j in range(degree):
		d += np.power(validX, degree-j) * Wlin[j]
	d += Wlin[degree]
	crossError = math.pow( np.square(np.subtract( d, validY )).mean(), 1/2 )
	#print("{}".format( crossError ))
	sum += crossError

	a = np.linspace(-3, 3, 100)
	b = 0
	for i in range(degree):
		b += np.power(a, degree-i) * Wlin[i]
	b += Wlin[degree]
	plt.plot(a, b, color = 'red')

print("cross-validation errors for five-fold : {}".format( sum/5 ))

plt.scatter(x[:trainPoint], y, color = 'blue')
plt.ylim(-8, 8)
plt.title('Fitting plots for cross-validation errors ( five-fold )\nwith degree - {D} and dataPoint {P}'.format( D=degree, P=dataPoint))
plt.xlabel("trainingPoint + validationPoint : blue")
plt.show()


import numpy as np
import sympy as sp
from sympy.core import S, Dummy, pi
from sympy.functions.elementary.trigonometric import sin, cos
from sympy.functions.special.gamma_functions import gamma
from sympy.polys.orthopolys import (legendre_poly, laguerre_poly,
                                    hermite_poly, jacobi_poly)
from sympy.polys.rootoftools import RootOf

def gauss_chebyshev_t(n, n_digits):
    r"""
    Computes the Gauss-Chebyshev quadrature [1]_ points and weights of
    the first kind.

    The Gauss-Chebyshev quadrature of the first kind approximates the integral:

    .. math::
        \int_{-1}^{1} \frac{1}{\sqrt{1-x^2}} f(x)\,dx \approx
            \sum_{i=1}^n w_i f(x_i)

    The nodes `x_i` of an order `n` quadrature rule are the roots of `T_n`
    and the weights `w_i` are given by:

    .. math::
        w_i = \frac{\pi}{n}

    Parameters
    ==========

    n : the order of quadrature

    n_digits : number of significant digits of the points and weights to return

    Returns
    =======

    (x, w) : the ``x`` and ``w`` are lists of points and weights as Floats.
             The points `x_i` and weights `w_i` are returned as ``(x, w)``
             tuple of lists.

    Examples
    ========

    >>> from sympy import S
    >>> from sympy.integrals.quadrature import gauss_chebyshev_t
    >>> x, w = gauss_chebyshev_t(3, 5)
    >>> x
    [0.86602, 0, -0.86602]
    >>> w
    [1.0472, 1.0472, 1.0472]

    >>> x, w = gauss_chebyshev_t(6, 5)
    >>> x
    [0.96593, 0.70711, 0.25882, -0.25882, -0.70711, -0.96593]
    >>> w
    [0.5236, 0.5236, 0.5236, 0.5236, 0.5236, 0.5236]

    See Also
    ========

    gauss_legendre, gauss_laguerre, gauss_hermite, gauss_gen_laguerre, gauss_chebyshev_u, gauss_jacobi, gauss_lobatto

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Chebyshev%E2%80%93Gauss_quadrature
    .. [2] http://people.sc.fsu.edu/~jburkardt/cpp_src/chebyshev1_rule/chebyshev1_rule.html
    """
    xi = []
    w = []
    for i in range(1, n+1):
        xi.append((cos((2*i-S.One)/(2*n)*S.Pi)).n(n_digits))
        w.append((S.Pi/n).n(n_digits))
    return xi, w

digits = 36
npoints = list(range(1,17))

for n in npoints:
    xi, wi = gauss_chebyshev_t(n, digits)

    print('\nN = %3d'%(n))
    for i in range(0,n):
        print('%3d %40.36f %40.36f'%(i+1,xi[i],wi[i]))


"""
newton.py - Implement fractals using newton-raphson approximation method
of solving functions.

"""

import matplotlib.pyplot as plt
import numpy as np
import math
import functools
import multiprocessing as mp

def rgb2int(rgb):
    red = rgb[0]
    green = rgb[1]
    blue = rgb[2]
    RGBint = (red << 16) + (green << 8) + blue
    return RGBint

def int2rgb(RGBint):
    blue =  RGBint & 255
    green = (RGBint >> 8) & 255
    red =   (RGBint >> 16) & 255
    return (red, green, blue)

# put any complex function here to generate a fractal for it!
def fcube(z):
    return z ** 3 - 1

def f8(z):
    return z**8 - 16

def sin(z):
    return math.sin(abs(z))

def cos(z):
    return math.cos(abs(z))

def circle(z):
    return math.sin(abs(z))**2 + math.cos(abs(z))**2

def tan(z):
    return math.tan(abs(z))

def cot(z):
    return 1.0/math.tan(abs(z))

def fcube2(z):
    return z**3 - 2*z

def fsquare(z):
    return z**2 - 1

def fquad(z):
    return z**4 - 1

def fsix(z):
    return z**6  + z**3 - 1

def fcomplex(z):
    return z**(0.5+0.3j) - 1

def ftrig1(z):
    return math.cos(abs(z)) - math.sin(abs(z))

def newton_set_calc_row(y, width, height, function, niter=256, x_off=0, y_off=0, zoom=1):
    """ Calculate one row of the newton set with size width x height """

    row_pixels = np.arange(width, dtype=np.uint32)
    # drawing area
    xa, xb, ya, yb = -2.5, 2.5, -2.5, 2.5

    zy = (y + y_off) * (yb - ya) / (zoom*(height - 1)) + ya

    h = 1e-7 # step size for numerical derivative
    eps = 1e-3 # max error allowed
    a = complex(1, 0)

    for x in range(width):
        # calculate the initial real and imaginary part of z,
        # based on the pixel location and zoom and position values
        zx = (x + x_off) * (xb - xa) / (zoom*(width - 1)) + xa
        z = complex(zx, zy)

        for i in range(niter):
            # complex numerical derivative
            dz = (function(z + complex(h, h)) - function(z)) / complex(h, h)
            if dz == 0:
                break

            znext = z - a*function(z) / dz # Newton iteration
            if abs(znext - z) < eps: # stop when close enough to any root
                break

            z = znext

        # Color according to iteration count
        rgb = (i % 16 * 32, i % 8 * 64, i % 4 * 64)
        row_pixels[x] = rgb2int(rgb)

    return y,row_pixels

# draw the fractal
def newton_set(width, height, zoom=1, x_off=0, y_off=0, niter=256):
    """ Fractals using newton-raphson """

    # drawing area
    xa, xb, ya, yb = -2.5, 2.5, -2.5, 2.5
    # Pixels array
    pixels = np.arange(width*height*3, dtype=np.uint32).reshape(height, width, 3)

    h = 1e-7 # step size for numerical derivative
    eps = 1e-3 # max error allowed

    # Bounding roots
    r1 = 1
    r2 = complex(-0.5, math.sin(2*math.pi/3))
    r3 = complex(-0.5, -1*math.sin(2*math.pi/3))

    # Color multiplication factor
    multcol = 5

    for y in range(height):
        zy = (y + y_off) * (yb - ya)/ (zoom*(height - 1)) + ya

        for x in range(width):
            zx = (x + x_off) * (xb - xa)/ (zoom*(width - 1)) + xa
            z = complex(zx, zy)
            count = 0

            for i in range(niter):
                # complex numerical derivative
                dz = (fcube(z + complex(h, h)) - fcube(z)) / complex(h, h)
                if dz == 0:
                    break

                count += 1
                if count > 255:
                    break

                znext = z - fcube(z) / dz # Newton iteration
                if abs(znext - z) < eps: # stop when close enough to any root
                    break

                z = znext

            # Pixels colored using the roots
            if abs(z-r1)<eps:
                pixels[y,x] = (255 - count*multcol, 0, 0)
            elif abs(z-r2)<=eps:
                pixels[y,x] = (0, 255 - count*multcol, 0)
            elif abs(z-r3)<=eps:
                pixels[y,x] = (0, 0, 255 - count*multcol)

    return pixels

def newton_set_mp(width, height, function, zoom=1, x_off=0, y_off=0, niter=256):
    """ Newton-raphson fractal set with multiprocessing """

    w,h = width, height
    pixels = np.arange(w*h*3, dtype=np.uint32).reshape(h, w, 3)

    # print('Starting calculation using',width, height,cx,cy)
    pool = mp.Pool(mp.cpu_count())

    newton_partial = functools.partial(newton_set_calc_row,
                                      width=width,height=height, function=function,
                                      niter=niter,zoom=zoom,x_off=x_off,y_off=y_off)

    for y,row_pixel in pool.map(newton_partial, range(h)):
        for x in range(w):
            pixels[y, x] = np.array(int2rgb(row_pixel[x]))

    return pixels

def display(function, width=1024, height=1024, niter=1024, zoom=1, x_off=0, y_off=0):
    """ Display a newton-raphson fractal """

    # pimg = newton_set(width, height, zoom=zoom, x_off=x_off, y_off=y_off, niter=niter)
    pimg = newton_set_mp(width, height, function, zoom=zoom,x_off=x_off, y_off=y_off, niter=niter)
    plt.axis('off')
    plt.imshow(pimg)
    plt.show()

def fsqr(z):
    return z**2 - 1

def fquad(z):
    return z**4 - 1

def fsix(z):
    return z**6  + z**3 - 1

def f8(z):
    return z**8  - 16

def fe(z):
    return math.e**z

def flog(z):
    return math.log(abs(z))

if __name__ == "__main__":
    display(f8)


'''
# 1) Реализовать метод Гаусса с оптимальным заполнением для разреженных матриц и
#    построением соответсвующего этому методу разложения матрицы системы A вида A=PLUP'; - DONE
# 2) при реализации учесь формат хранения A; - DONE
# 3) реализовать для этого метода итерационное уточнение простой и переменной точности,
#    оценить качество получаемых результатов; - DONE
'''

import numpy as np
import matplotlib.pyplot as plt
from scipy.linalg import solve as npsolve
from scipy.sparse.base import spmatrix
from collections import defaultdict

import utils
from utils import MatrixBuilder
from task1 import PLUP_decomposition, Sparse_decomposition
from task3 import plu_solve

test_matrix = np.matrix([
    [1, 1, 1],
    [2, 2, 5],
    [20, 6, 8],
], dtype=np.float)

test_vector = np.array([
    1,
    2,
    3,
])

# http://www.ms.unimelb.edu.au/~carniesl/coursedocs/432Assets/MatrixNorms.pdf p.13
norm = lambda A: np.linalg.norm(A, ord=np.inf)
norm1 = lambda A: np.linalg.norm(A, ord=1)

def relative_residual(A, x, b):
    r = b - A.dot(x)
    return norm(r) / norm(b)

#
# Solve equation system Ax=b by using A=PLUP' decomposition
#
# Если аргумент sparse не указан, то выбор разложения выбирается автоматически
# по типу матрицы A.
#
# Если хочется заставить функцию использовать
# PLUP разложение для sparse матрицы (или sparse разложение для np.matrix),
# то можно указать plup_solve(..., ..., sparse=True/False)
#
def plup_solve(A: np.matrix, b: np.array, sparse=None) -> np.array:
    A = A
    b = b

    if sparse == None:
        sparse = True if isinstance(A, spmatrix) else False

    P,L,U,P_ = Sparse_decomposition(A) if sparse else PLUP_decomposition(A)
    Pb = P.transpose().dot(b)

    y = np.zeros(b.size)
    for m in range(0, b.size):
        y[m] = Pb[m] - sum(
            L[m][i] * y[i] for i in range(0, m)
        )
        y[m] /= L[m][m]

    z = np.zeros(b.size)
    for m in reversed(range(0, b.size)):
        z[m] = y[m] - sum(
            U[m][i] * z[i] for i in range(m+1, b.size)
        )
        z[m] /= U[m][m]

    x = P_.transpose().dot(z)

    return x

# Итерационное уточнение - стр. 34, два пункта сверху
def iterative_refinement(A: np.matrix, b: np.array, solver, iterations=1, double_precision=False, sparse=False) -> np.array:
    x = solver(A, b) if not sparse else solver(A, b, sparse=True)

    for i in range(0, iterations):
        chosen_type = np.longfloat if double_precision else A.dtype
        r = b.astype(chosen_type) - A.astype(chosen_type).dot(x).astype(chosen_type)
        z = solver(A, r.astype(A.dtype))
        x = x + z

    return x

#
# Average solution deviation (absolute) for system Ax=b
# Среднее значение модулей элементов вектора невязки
#
def solution_deviation(A:np.matrix, x:np.array, b:np.array):
    differences = b - A.dot(x)
    return np.mean( [abs(err) for err in differences] )

#
# Generates non-singular equation system in form Ax=b,
#
def generate_test_system(size=100, sparse=False):
    vector = utils.get_random_vector(size, low=-100000, high=100000)

    if not sparse:
        matrix = MatrixBuilder(size, low=-100000, high=100000).nonsingular().gen()
        #matrix = MatrixBuilder(size).nonsingular().nearsingular().gen()
    else:
        matrix = MatrixBuilder(size).nonsingular().randsparse().gen()

    return matrix, vector

def check_whether_custum_solver_works(solver):
    A, b = test_matrix, test_vector
    return np.allclose(plup_solve(A,b), npsolve(A, b))

def precision_tests(A, b):
    # Here we check how precise are solutions obtained by different solvers

    numpyResult = npsolve(A, b)
    ourResult = plup_solve(A, b, sparse=False)
    sparseResult = plup_solve(A, b, sparse=True)

    ourBetterResult = iterative_refinement(A, b, solver=plup_solve, double_precision=False)
    ourEvenBetterResult = iterative_refinement(A, b, solver=plup_solve, double_precision=True)

    results = [
        ('NumPy solver', numpyResult),
        ('PLUP solver', ourResult),
        ('PLUP sparse solver', sparseResult),
        ('PLUP solver with iterative refinement', ourBetterResult),
        ('PLUP solver with iterative refinement and double precision', ourEvenBetterResult),
    ]
    for result in results:
        description, x = result
        print(solution_deviation(A, x, b), description)

def check_solvers():
    systems = []

    for i in range(0, 300):
        A, b = generate_test_system(size=30, sparse=False)
        systems.append( (A, b) )

    results = defaultdict(list)
    for system in systems:
        A, b = system

        plup = plup_solve(A, b)
        plup_sparse = plup_solve(A, b, sparse=True)
        plu  = plu_solve(A, b)
        nump = npsolve(A, b)

        results['plup'].append(relative_residual(A, plup, b))
        results['plup_parse'].append(relative_residual(A, plup_sparse, b))
        results['plu'].append(relative_residual(A, plu, b))
        results['nump'].append(relative_residual(A, nump, b))

    avgs = []
    for dp in ['plup', 'plup_parse', 'plu', 'nump']:
        avg = np.mean(results[dp])
        avgs.append((dp, avg))

    print(avgs)

if __name__ == '__main__':
    #A, b = generate_test_system(size=50, sparse=False)
    check_solvers()
    exit()
    # precision_tests(A, b)

    # lresults = []
    # for i in range(0, 10):
    #     x = iterative_refinement(A, b, solver=plup_solve, iterations=i)
    #     deviation = solution_deviation(A, x, b)
    #     lresults.append((i, deviation))
    #
    # plt.plot(*zip(*lresults))
    # plt.show()
    #
    # exit()

    systems = []
    for i in range(0, 300):
        A, b = generate_test_system(size=25, sparse=False)
        systems.append( (A, b) )

    iterations = 10
    results = defaultdict(list)
    lresults = []

    for system in systems:
        A, b = system
        for dp in [False, True]:
            x = iterative_refinement(
                A, b,
                solver=plup_solve,
                iterations=1,
                double_precision=dp
            )

            deviation = solution_deviation(A, x, b)
            rel_res = relative_residual(A, x, b)

            results[dp].append(rel_res)
            lresults.append((dp, rel_res))

            #print(i, rel_res)

    avgs = []
    for dp in [False, True]:
        avg = np.mean(results[dp])
        avgs.append((dp, avg))

    print(avgs[0])
    print(avgs[1])

    #plt.plot(*zip(*avgs))
    # plt.plot(*zip(*lresults))
    # plt.xlabel('X-Axis')
    # plt.ylabel('Y-Axis')
    # plt.axis([-1, 5, 0, 1e-13])
    #plt.show()


# 11 Exercise Snippets

# 11.1. What does the following code do?
def mystery(a, b):
    if b == 1:
        return a
    else:
        return a + mystery(a, b - 1)

mystery(2, 10)

# 11.2. Find the logic error(s) in the following recursive function,
# and explain how to correct it (them). This function should find
# the sum of the values from 0 to `n`.
def sum(n):
    if n == 0:
        return 0
    else:
        return n + sum(n)

# 11.3. What does the following code do?
def mystery(a_array, size):
    if size == 1:
        return a_array[0]
    else:
        return a_array[size - 1] + mystery(a_array, size - 1)

import numpy as np

numbers = np.arange(1, 11)

mystery(numbers, len(numbers))

##########################################################################
# (C) Copyright 2019 by Deitel & Associates, Inc. and                    #
# Pearson Education, Inc. All Rights Reserved.                           #
#                                                                        #
# DISCLAIMER: The authors and publisher of this book have used their     #
# best efforts in preparing the book. These efforts include the          #
# development, research, and testing of the theories and programs        #
# to determine their effectiveness. The authors and publisher make       #
# no warranty of any kind, expressed or implied, with regard to these    #
# programs or to the documentation contained in these books. The authors #
# and publisher shall not be liable in any event for incidental or       #
# consequential damages in connection with, or arising out of, the       #
# furnishing, performance, or use of these programs.                     #
##########################################################################


from math import sqrt

import matplotlib.pyplot as plt
import numpy as np
from prettytable import PrettyTable

headers = PrettyTable(['№', 'x1', 'x2', 'f(x1,x2)', 'df'])

def function(x, y):
    return 22 * x + 0.1 * y + np.exp(4.84 * x * x + 1.2 * y * y)

def extremum(a, b):
    return (a + b) / 2

def table(n, x1, x2, new_function, old_function):
    table_list = {
        'n': n,
        'x1': round(x1, 8),
        'x2': round(x2, 8),
        'f(x1,x2)': round(new_function, 10),
        'df': round(abs(new_function - old_function), 10)
    }
    headers.add_row(table_list.values())

def gss_x1(a, b, x2, eps):
    interval = b - a
    s = (sqrt(5) - 1) / 2
    a1 = a + interval * (1 - s)
    b1 = a + interval * s
    fa1 = function(a1, x2)
    fb1 = function(b1, x2)
    while abs(b - a) >= eps:
        if fa1 <= fb1:  # <= - минимум, >= - максимум
            b = b1
            b1 = a1
            fb1 = fa1
            interval = interval * s
            a1 = a + interval * (1 - s)
            fa1 = function(a1, x2)
        else:
            a = a1
            a1 = b1
            fa1 = fb1
            interval = interval * s
            b1 = a + interval * s
            fb1 = function(b1, x2)
    return extremum(a, b)

def gss_x2(c, d, x1, eps):
    interval = d - c
    s = (sqrt(5) - 1) / 2
    c1 = c + interval * (1 - s)
    d1 = c + interval * s
    fc1 = function(x1, c1)
    fd1 = function(x1, d1)
    while abs(d - c) >= eps:
        if fc1 <= fd1:  # <= - минимум, >= - максимум
            d = d1
            d1 = c1
            fd1 = fc1
            interval = interval * s
            c1 = c + interval * (1 - s)
            fc1 = function(x1, c1)
        else:
            c = c1
            c1 = d1
            fc1 = fd1
            interval = interval * s
            d1 = c + interval * s
            fd1 = function(x1, d1)
    return extremum(c, d)

def result(eps, x1, x2, new_function):
    return f'eps = {eps}\nx1 = {x1}, x2 = {x2}\nf(x1,x2) = {new_function}'

def coordinate_search(x1, x2, dx, eps):
    points_x = []
    points_y = []
    func = []
    count = 0
    k = 5
    eps_y = k * eps
    new_function = function(x1, x2)
    old_function = new_function + 1
    points_x.append(x1)
    points_y.append(x2)
    while abs(new_function - old_function) > eps_y:
        old_function = new_function
        count += 1
        a, c = x1 - dx, x2 - dx
        b, d = x1 + dx, x2 + dx

        x1 = gss_x1(a, b, x2, eps)
        new_function = function(x1, x2)
        points_x.append(x1)
        points_y.append(x2)
        func.append(function(points_x[-1], points_y[-1]))
        table(count, x1, x2, new_function, old_function)
        x2 = gss_x2(c, d, x1, eps)
        new_function = function(x1, x2)
        table('-', x1, x2, new_function, old_function)
        points_x.append(x1)
        points_y.append(x2)
        func.append(function(points_x[-1], points_y[-1]))
    return result(eps, x1, x2, new_function), [points_x, points_y], func

if __name__ == '__main__':
    x0 = y0 = -1
    dx = 2
    epsilon = 0.000000001
    info, points_coord, coord_func = coordinate_search(x0, y0, dx, epsilon)
    print(headers)
    print(info)

    x_axis = y_axis = np.arange(-1 * 1.1, 1 * 1.1, 0.0005)

    X, Y = np.meshgrid(x_axis, y_axis)
    Zs = np.array(function(np.ravel(X), np.ravel(Y)))
    Z = Zs.reshape(X.shape)
    coord_func.sort()
    cs = plt.contour(X, Y, Z, levels=coord_func)
    plt.clabel(cs)
    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.plot(points_coord[0], points_coord[1])

    plt.show()


import numpy as np

a = np.array([[50,10,30],
              [60,20,40],
              [70,80,90]])
# return simple average of all element from an array
print()
print(np.mean(a))

# return simple average of column element from an array

print()
print(np.mean(a,axis=0))

# return simple average of row element from an array

print()
print(np.mean(a,axis=1))


# Linear Regression

from scipy.stats import linregress
X = [3, 4, 5, 6, 7, 8]
Y = [0, 7, 17, 26, 35, 45]

L = linregress(X,Y)
print('linregress(X,Y) =', L, end='\n\n')

a0 = L.intercept
a1 = L.slope
print('y = linregress(X,Y).intercept + linregress(X,Y).slope x')
print('y = %f %+f x' % (a0, a1), end='\n\n')

# Polynomial Regression

from scipy.optimize import curve_fit
X = [0, 1, 2, 3, 4, 5]
Y = [2, 8, 14, 28, 39, 62]

# For Quadratic Polynomial
f = lambda x, a0, a1, a2: a0 + a1*x + a2*x**2

a,_ = curve_fit(f, X, Y)                # a,_ neglects the 2nd array
print('a,_ = curve_fit(f, X, Y)')       # of absolute differences
print('y = a[0] + a[1] x + a[2] x^2')
print('y = %f %+f x %+f x^2' %(a[0],a[1],a[2]), end='\n\n')

# For Cubic Polynomial
f = lambda x, a0, a1, a2, a3: a0 + a1*x + a2*x**2 + a3*x**3

a,_ = curve_fit(f, X, Y)
print('a,_ = curve_fit(f, X, Y)')
print('y = a[0] + a[1] x + a[2] x^2 + a[3] x^3')
print('y = %f %+f x %+f x^2 %+f x^3' %(a[0],a[1],a[2],a[3]), end='\n\n')

2. Interpolation and Curve Fitting/4. Curve Fitting functions of SciPy.py