import numpy as np
from scipy.io import loadmat # for loading MATLAB files
import pandas as pd

def costfunction(Theta1, Theta2, X_with_bias, y_one_hot, lambda_param):

    # Feedforward
    a1 = X_with_bias # 5000x401

    z2 = Theta1.dot(a1.T)  # 25x401 * 401x5000 = 25x5000
    a2 = np.c_[np.ones((X_with_bias.shape[0], 1)), sigmoid(z2.T)]  # 5000x26

    z3 = Theta2.dot(a2.T)  # 10x26 * 26x5000 = 10x5000
    a3 = sigmoid(z3)  # 10x5000

    # Compute the cost
    number_of_examples = X_with_bias.shape[0]

    J = -1 * (1 / number_of_examples) * np.sum((np.log(a3.T) * (y_one_hot) + np.log(1 - a3).T * (1 - y_one_hot))) + \
        (lambda_param/(2 * number_of_examples))*(np.sum(np.square(Theta1[:,1:])) + np.sum(np.square(Theta2[:,1:])))

    # Compute the gradients
    delta3 = a3.T - y_one_hot  # 5000x10
    delta2 = Theta2[:, 1:].T.dot(delta3.T) * sigmoidGradient(z2)  # 25x10 *10x5000 * 25x5000 = 25x5000

    big_delta1 = delta2.dot(a1)  # 25x5000 * 5000x401 = 25x401
    big_delta2 = delta3.T.dot(a2)  # 10x5000 *5000x26 = 10x26

    # DON'T regularize the fist column of Theta1 and Theta2
    Theta1_ = np.c_[np.ones((Theta1.shape[0], 1)), Theta1[:, 1:]]
    Theta2_ = np.c_[np.ones((Theta2.shape[0], 1)), Theta2[:, 1:]]

    theta1_grad = # Put your code here
    theta2_grad = # Put your code here

    return J, theta1_grad, theta2_grad

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

def sigmoidGradient(z):
    return sigmoid(z) * (1 - sigmoid(z))

'''
Step 0: Do the following steps:
1. load the training dataset from 'data/digits.mat' and store as (X, y)
2. load the provided weights from 'data/weights.mat' and store as Theta1, Theta2
'''
dataset = loadmat('data/digits.mat')
print(dataset.keys())

weights = loadmat('data/weights.mat')
print(weights.keys())

X, y = dataset['X'], dataset['y']
print('X: {}'.format(X.shape))
print('y: {}'.format(y.shape))

Theta1, Theta2 = weights['Theta1'], weights['Theta2']
print('Theta1: {}'.format(Theta1.shape))
print('Theta2: {}'.format(Theta2.shape))

'''
Step 1: Augment the original matrix X with 1 (the intercept term)
'''

# Add constant for intercept
X_new = np.c_[np.ones((dataset['X'].shape[0], 1)), dataset['X']]
print('X_new (with intercept): {}'.format(X_new.shape), '\n')

'''
Step 2: Convert the original labels to one-hot vectors
'''
y_one_hot = pd.get_dummies(y.ravel()).values

'''
Step 3: Compute the cost function with lambda = 1
'''
print('The cost (without regularization) is', costfunction(Theta1, Theta2, X_new, y_one_hot, 1)[0], '\n')

'''
Step 4: Test the implemented sigmoidGradient.
For large values (both positive and negative), the gradient should be close to 0.
Furthermore, when z = 0, the gradient should be exactly 0.25.
'''
print('Compute the sigmoidGradient for -1, -0.5, 0, 0.5, 1 :', '\n', [sigmoidGradient(z) for z in [-1, -0.5, 0, 0.5, 1]])


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

## Machine Learning Online Class
#  Exercise 1: Linear regression with multiple variables

def featureNormalize(X):
    mean = np.mean(X, 0)
    sigma = np.std(X, 0)
    X_norm = (X - mean) / sigma
    return (X_norm, mean, sigma)

def computeCostMulti(X, y, theta):
    error = (X @ theta - y)
    return (error.T @ error)/ (2*y.size)

def gradientDescentMulti(X, y, theta, alpha, num_iters):
    m = y.size
    J_history = np.zeros(num_iters)
    for iter in range(num_iters):
        theta -= alpha/m * X.T @ (X @ theta - y)
        J_history[iter] = computeCostMulti(X, y, theta)
    return (theta, J_history)

def normalEqn(X,y):
    return np.linalg.pinv(X.T @ X) @ X.T @ y

# ================ Part 1: Feature Normalization ================

print('Loading data ...')
# Load Data
scriptdir = os.path.dirname(os.path.realpath(__file__))
data = np.loadtxt(scriptdir + '//ex1data2.txt', delimiter=',')
X = data[:,:-1]
y = data[:,-1]

# Print out some data points
print('First 10 examples from the dataset: X')
for i in range(10):
    print(f'x = {X[i,:]}, y = {y[i]}')
input('Program paused. Press enter to continue.\n')

# Scale features and set them to zero mean
print('Normalizing Features ...')
X, mean, sigma = featureNormalize(X)
##Add intercept term to X
X = np.c_[np.ones(y.size), X]

# ================ Part 2: Gradient Descent ================
print('Running gradient descent ...')
alpha = [0.01, 0.03, 0.10, 0.3, 1, 1.25]
for i in range(len(alpha)):
    #Choose some alpha value
    num_iters = 400

    #Init Theta and Run Gradient Descent
    theta = np.zeros(3)
    theta, J_history = gradientDescentMulti(X, y, theta, alpha[i], num_iters)

    #Plot the convergence graph
    plt.plot(J_history, lw=2, label=f'Cost J for alpha {alpha[i]}')

    #Display gradient descent's result
    print(f'Theta computed from gradient descent with alpha {alpha[i]}')
    print(f'{theta}\n')
plt.xlabel('Number of iterations')
plt.ylabel('Cost J')
plt.legend()
plt.show()

# Estimate the price of a 1650 sq-ft, 3 br house
house = [1650, 3]
house_norm = ((house - mean) / sigma)
price = np.append(1, house_norm) @ theta
print(f'Predicted price of a 1650 sq-ft, 3 br house (using gradient descent):\n ${price}')
input('Program paused. Press enter to continue.\n')

# ================ Part 3: Normal Equations ================

print('Solving with normal equations...')

X = data[:,:-1]
y = data[:,-1]

#Add intercept term to X
X = np.c_[np.ones(y.size), X]

# Calculate the parameters from the normal equation
theta = normalEqn(X, y)

# Display normal equation's result
print('Theta computed from the normal equations:')
print(theta)
input('Program paused. Press enter to continue.\n')

# Estimate the price of a 1650 sq-ft, 3 br house
house = [1650, 3]
price = np.append(1, house) @ theta
print(f'Predicted price of a 1650 sq-ft, 3 br house (using normal equations):\n ${price}')


# -*- coding: utf-8 -*-
import numpy as np
from scipy.optimize import leastsq
import pylab as pl

def func(x, p):
    """
    数据拟合所用的函数: A*sin(2*pi*k*x + theta)
    """
    A, k, theta = p
    return A*np.sin(2*np.pi*k*x+theta)

def residuals(p, y, x):
    """
    实验数据x, y和拟合函数之间的差，p为拟合需要找到的系数
    """
    return y - func(x, p)

x = np.linspace(0, -2*np.pi, 100)
A, k, theta = 10, 0.34, np.pi/6 # 真实数据的函数参数
y0 = func(x, [A, k, theta]) # 真实数据
y1 = y0 + 2 * np.random.randn(len(x)) # 加入噪声之后的实验数据

p0 = [7, 0.2, 0] # 第一次猜测的函数拟合参数

# 调用leastsq进行数据拟合
# residuals为计算误差的函数
# p0为拟合参数的初始值
# args为需要拟合的实验数据
plsq = leastsq(residuals, p0, args=(y1, x))

print u"real parameter:", [A, k, theta]
print u"fitted parameter", plsq[0] # 实验数据拟合后的参数

pl.plot(x, y0, label=u"real data")
pl.plot(x, y1, label=u"noisy data")
pl.plot(x, func(x, plsq[0]), label=u"fitted data")
pl.legend()
pl.show()


"""
This module illustrate how to find the roots of an equation using
the Newton-Rapshon and secant methods available in the Scipy lib.
"""
from scipy import optimize
from math import exp

def f(x):
    """Represents a mathematical function called f"""
    return x*(x - 2)*exp(3 - x)

def fp(x):
    """Represents the first derivative of f"""
    return -(x**2 - 4*x + 2)*exp(3 - x)

roots_secant = optimize.newton(f, 1., x1=1.5) # Using x1 = 1.5 and the secant
roots_newton = optimize.newton(f, 1., fprime=fp) # Using Newton-Rapshon method
roots_bisect = optimize.bisect(f, 1., 3) # Using bisect
roots_brent  = optimize.brentq(f, 1., 3.) # Using Brent

print(roots_secant)
print(roots_newton)
print(roots_bisect)
print(roots_brent)


from src.functions.objectivefn import ObjectiveFn
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

class HimmelbauFn(ObjectiveFn):

    def __init__(self):
        super(HimmelbauFn, self).__init__(2)
        self.domain = (-5.0, 5.0)

    def get_minima(self):
        # minima = (3.0, 2.0)             # Minima point 1
        # minima = (-2.805118, 3.131312)  # Minima point 2
        # minima = (-3.779310, -3.283186) # Minima point 3
        minima = (3.584428, -1.848126)  # Minima point 4
        return self.evaluate(minima)  # minima value is 0 at the 4 global minimas

    def evaluate(self, params):
        f = 0
        try:
            if len(params) != self.dims:
                raise Exception('Dimensions not matching params')
            X = params[0]
            Y = params[1]
            f = (X ** 2 + Y - 11) ** 2 + (X + Y ** 2 - 7) ** 2
            return f
        except Exception as error:
            print('Exception in sphere_fn.eval_fn: ', repr(error))

    def graph_fn(self):
        # X = np.linspace(-10, 10, 100)
        # Y = np.linspace(-10, 10, 100)
        X = np.linspace(self.domain[0], self.domain[1], 200)
        Y = np.linspace(self.domain[0], self.domain[1], 200)
        X, Y = np.meshgrid(X, Y)
        Z = (X ** 2 + Y - 11) ** 2 + (X + Y ** 2 - 7) ** 2

        fig = plt.figure()
        ax = fig.gca(projection='3d')
        ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.plasma, linewidth=0, antialiased=False)
        plt.savefig('himmelbau.png')

    def contour_plot(self, save_file_name, points):
        # X = np.linspace(-10, 10, 100)
        # Y = np.linspace(-10, 10, 100)
        # X = np.linspace(-5, 5, 200)
        # Y = np.linspace(-5, 5, 200)
        X = np.linspace(self.domain[0], self.domain[1], 100)
        Y = np.linspace(self.domain[0], self.domain[1], 100)
        X, Y = np.meshgrid(X, Y)
        Z = (X ** 2 + Y - 11) ** 2 + (X + Y ** 2 - 7) ** 2

        plt.contour(X, Y, Z)
        for point in points:
            plt.scatter(point[0], point[1], marker='X', color='red')
        plt.savefig(save_file_name, dpi=300, bbox_inches='tight')
        plt.close()

    def is_defined_only_for_2d(self):
        return True

    def name(self):
        return "HimmelbauFn"


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

# # Approximating functions on $R$
#
# **Randall Romero Aguilar, PhD**
#
# This demo is based on the original Matlab demo accompanying the  Computational Economics and Finance 2001 textbook by Mario Miranda and Paul Fackler.
#
# Original (Matlab) CompEcon file: **demapp01.m**
#
# Running this file requires the Python version of CompEcon. This can be installed with pip by running
#
#     !pip install compecon --upgrade
#
# Last updated: 2021-Oct-01
#

# ## About
#
# This demo illustrates how to use CompEcon Toolbox routines to construct and operate with an approximant for a function defined on an interval of the real line.
#
# In particular, we construct an approximant for $f(x)=\exp(-x)$ on the interval $[-1,1]$.  The function used in this illustration posseses a closed-form, which will allow us to measure approximation error precisely. Of course, in practical applications, the function to be approximated will not possess a known closed-form.
#
# In order to carry out the exercise, one must first code the function to be approximated at arbitrary points.
# Let's begin:

# ## Initial tasks

# In[1]:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from compecon import BasisChebyshev, BasisSpline, demo

# ### Defining the functions

# Function to be approximated and derivatives

# In[2]:

def f(x): return np.exp(-x)
def d1(x): return -np.exp(-x)
def d2(x): return np.exp(-x)

# Set degree and domain of interpolation

# In[3]:

n, a, b = 10, -1, 1

# ## Chebyshev interpolation

# In this case, let us use an order 10 Chebychev approximation scheme:

# In[4]:

F = BasisChebyshev(n, a, b, f=f)

# One may now evaluate the approximant at any point x calling the basis:

# In[5]:

x = 0
F(x)

# ... one may also evaluate the approximant's first and second derivatives at x:

# In[6]:

print(f'1st derivative = {F(x, 1):.4f}, 2nd derivative = {F(x, 2):.4f}')

# ... and one may even evaluate the approximant's definite integral between the left endpoint a and x:

# In[7]:

F(x, -1)

# ###  Compare analytic and numerical computations

# In[8]:

pd.set_option('precision', 8)

pd.DataFrame({
    'Numerical': [F(x), F(x, 1), F(x,2), F(x, -1)],
    'Analytic': [f(x), d1(x), d2(x), np.exp(1)-1]},
    index = ['Function', 'First Derivative', 'Second Derivative', 'Definite Integral']
)

# ### Plots of approximation errors

# One may evaluate the accuracy of the Chebychev polynomial approximant by computing the approximation error on a highly refined grid of points:

# In[9]:

nplot = 501  # number of grid nodes
xgrid = np.linspace(a, b, nplot)    # generate refined grid for plotting

figures=[]  # to save all figures

def approx_error(true_func, appr_func, d=0, title=''):
    fig, ax = plt.subplots()
    ax.hlines(0,a,b, 'k', linestyle='--',linewidth=2)
    ax.plot(xgrid, appr_func(xgrid, d) - true_func(xgrid))
    ax.ticklabel_format(style='sci', axis='y', scilimits=(0,0))
    ax.set(title=title, xlabel='x', ylabel='Error')

    figures.append(plt.gcf())

# ### Plot function approximation error

# In[10]:

approx_error(f,F,title='Chebychev Approximation Error - Function')

# The plot indicates that an order 10 Chebychev approximation scheme, produces approximation errors no bigger in magnitude than $6\times10^{-10}$. The approximation error exhibits the "Chebychev equioscillation property", oscilating relatively uniformly throughout the approximation domain.
#
# This commonly occurs when function being approximated is very smooth, as is the case here but should not be expected when the function is not smooth.  Further notice how the approximation error is exactly 0 at the approximation nodes --- which is true by contruction.

# ### Plot first derivative approximation error

# In[11]:

approx_error(d1,F,1, title='Chebychev Approximation Error - First Derivative')

# ### Plot second derivative approximation error

# In[12]:

approx_error(d2,F,2, title='Chebychev Approximation Error - Second Derivative')

# ## Cubic spline interpolation

# Let us repeat the approximation exercise, this time constructing a 21-function cubic spline approximant:

# In[13]:

n = 21                         # order of approximation
S = BasisSpline(n, a, b, f=f)  # define basis
yapp = S(xgrid)                # approximant values at grid nodes

# ### Plot function approximation error

# In[14]:

approx_error(f,S,title='Cubic Spline Approximation Error - Function')

# The plot indicates that an order 21 cubic spline approximation scheme produces approximation errors no bigger in magnitude than $1.2\times10^{-6}$, about four orders of magnitude worse than with Chebychev polynomials.

# ### Plot first derivative approximation error

# In[15]:

approx_error(d1,S,1, title='Cubic Spline Approximation Error - First Derivative')

# ### Plot second derivative approximation error

# In[16]:

approx_error(d2,S,2, title='Cubic Spline Approximation Error - Second Derivative')

# ## Linear spline interpolation

# Let us repeat the approximation exercise, this time constructing a 31-function linear spline approximant:

# In[17]:

n = 31
L = BasisSpline(n, a, b, k=1, f=f)

# ### Plot function approximation error

# In[18]:

approx_error(f,L,title='Linear Spline Approximation Error - Function')

# The plot indicates that an order 21 cubic spline approximation scheme produces approximation errors no bigger in magnitude than $1.2\times10^{-6}$, about four orders of magnitude worse than with Chebychev polynomials.

# ### Save all figures to disc

# In[19]:

#demo.savefig(figures,name="demapp01")

_build/jupyter_execute/notebooks/app/01 Approximating function on R.py


# -*- coding: utf-8 -*-
"""
chebpy.chebq
============

Chebyshev quadrature.

"""

import numpy as np
from numpy.fft import ifft

__all__ = ['cheb_quadrature_cgl',
           'clencurt_weights',
           'clencurt_weights_fft',
           'cheb_quadrature_clencurt',
          ]

def cheb_quadrature_cgl(f):
    '''
    Chebyshev-Gauss 2nd kind quadrature.
    \integrate[-1,+1] sqrt(1-x^2) g(x) dx ~ \sum_i{1}{N-1} w_i * g(x_i)
    on
        x_i = cos(i*pi/N), i = 0, 1, 2, ..., N
    with
        w_i = (pi/N) * (1 - x_i^2), i = 1, 2, ..., N-1

    Note: from numeric experiments, this method is much less accurate than
    Clenshaw-Curtis quadrature.
    '''

    N = np.size(f) - 1
    theta = np.arange(N+1) * np.pi / N
    x = np.cos(theta)
    w = np.zeros_like(x)
    w += np.pi / N
    #w = np.pi/N
    w[0] *= .5
    w[N] *= .5
    #return np.dot(w[1:N], f[1:N] * np.sqrt(1-x[1:N]**2))
    return np.dot(w, f * np.sqrt(1-x**2))
    #return np.dot(w[1:N], f[1:N])

def clencurt_weights(N):
    '''
    The clenshaw-Curtis quadrature weights are:
        w_n = (4/(r_n*N)) * \sum_{k=0,even}{N} (1/r_k * cos(pi*k*n/N) /
                (1 - k^2), n = 0, 1, 2, ..., N
    Thus, w_0 = w_N, and w_0 = 1 / (N^2 - 1) for even N, w_0 = 1 / N^2
    for odd N.

    Ref. Trefethen LN, 2000, p128.
    '''

    theta = np.arange(N+1) * np.pi / N
    x = np.cos(theta)
    w = np.zeros_like(x)
    ii = np.arange(1,N)
    v = np.ones(N-1)
    if N % 2 == 0:
        w[0] = 1. / (N**2 - 1)
        w[N] = w[0]
        for k in xrange(1,N/2):
            v -= 2. * np.cos(2*k*theta[ii]) / (4*k**2 - 1.)
        v -= np.cos(N*theta[ii]) / (N**2 - 1.)
    else:
        w[0] = 1. / N**2
        w[N] = w[0]
        for k in xrange(1,(N-1)/2+1):
            v -= 2. * np.cos(2*k*theta[ii]) / (4*k**2 - 1.)
    w[ii] = 2 * v / N
    return w

def clencurt_weights_fft(N):
    '''
    Compute the (N+1) nodes and weights for Clenshaw-Curtis quadrature
    on the interval [-1, 1]. Using FFT algorithm, the weights and nodes are
    computed in linear time.
    '''

    c = np.zeros(N+1)
    if N % 2 == 0:
        c[0:N+2:2] = 2. / (1. - np.arange(0, N+2, 2)**2)
    else:
        c[0:N:2] = 2. / (1. - np.arange(0, N, 2)**2)
    c = np.concatenate((c, np.flipud(c[1:N])))

    v = np.real(ifft(c))

    w = 2. * v[0:N+1]
    w[0] *= .5
    w[N] *= .5

    return w

def cheb_quadrature_clencurt(f, w=None):
    '''
    Integrate f in the range [-1, 1] by Clenshaw-Curtis quadrature.
    '''
    N = np.size(f) - 1
    if w is None:
        w = clencurt_weights_fft(N)
    return np.dot(w, f)


import numpy as np
import math

class Impedance:
    """
    Used in ex_2_3.
    """

    def __init__(self, R, X):
        """
        Impedance constructor.

        :param R: Resistance [Ohm].
        :param X: Reactance [Ohm].
        """
        self.R = R
        self.X = X
        self.Z = complex(R, X)  # representation as a complex number.
        self.mag = np.abs(self.Z)  # Magnitude.
        self.ang = np.angle(self.Z)  # Angle in rad.

    @staticmethod
    def from_polar(mag, ang):
        """
        Use this if you want to build an impedance from its magnitude - angle description.
        :param mag: Magnitude [Ohm].
        :param ang: Angle [rad].
        :return: An Impedance object.
        """
        return Impedance(mag * np.cos(ang), mag * np.sin(ang))

    def __add__(self, other):
        """
        Override addition.

        :param other: Another impedance.
        :return: The sum of this and the other impedance.
        """
        return Impedance(self.R + other.R, self.X + other.X)

    def __mul__(self, other):
        """
        Override multiplication

        :param other: Another impedance.
        :return: The product of this and the other impedance.
        """
        # Could multiply the Z simply, but to show the details.
        mag = self.mag * other.mag
        ang = self.ang + other.ang
        return self.from_polar(mag, ang)

    def __truediv__(self, other):
        """
        Override (true) division

        :param other: Another impedance.
        :return: The quotient of this and the other impedance.
        """
        # Could devide the Z simply, but to show the details.
        mag = self.mag / other.mag
        ang = self.ang - other.ang
        return self.from_polar(mag, ang)

    def parallel(self, other):
        """
        Compute the equivalent impedance of this impedance in parallel with another.

        :param other: Another impedance.
        :return: The equivalent impedance.
        """
        num = self * other
        den = self + other
        return num / den

    def tex_cart(self):
        """
        Print impedance in latex format, R+jX style.
        """
        print("$ %.2f+j%.2f $" % (self.R, self.X))

    def tex_polar(self):
        """
        Print impedance in latex format, polar style.
        """
        print("$ %.2f \\angle %.2f $" % (self.mag, np.rad2deg(self.ang)))

    def __str__(self):
        return '%.2f < %.2f deg' % (self.mag, self.ang*180/math.pi)


# Copyright 2021 CR.Sparse Development Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from jax import jit, grad, lax

import jax.numpy as jnp
import cr.nimble as cnb

from .smooth import build2

def smooth_huber(tau=1.):
    r"""Huber penalty function and its gradient
    """
    tau = jnp.asarray(tau)
    tau = cnb.promote_arg_dtypes(tau)

    @jit
    def func(x):
        x = jnp.asarray(x)
        x = cnb.promote_arg_dtypes(x)
        x_mag = jnp.abs(x)

        small = x_mag <= tau
        x_small = 0.5*(x_mag**2)/tau
        x_large = x_mag - tau/2

        v = jnp.where(small, x_small, x_large)
        return sum(v)

    @jit
    def gradient(x):
        x = jnp.asarray(x)
        x = cnb.promote_arg_dtypes(x)
        g = x/jnp.maximum(tau, jnp.abs(x) )
        return g

    return build2(func, gradient)


from numpy import dot, zeros
from scipy import tril, triu, exp
from numpy.linalg import norm
from scipy.linalg import solve
from pylab import plot, clf, axis, legend, grid, loglog

#Perform newton's method with multiple equations
def newton_multi(F, J, x0, f, dfdy, tn, yn, dt, c, a, tol = 1e-8):

  #start with initial guess
  x_old = x0

  #Loop until break condition met
  while 1:
    #Find Jacobian
    M = J(dfdy, tn, yn, dt, c, a, x_old)
    #Find RHS(-F)
    RHS = dot(-1 , F(f, tn, yn, dt, c, a, x_old))

    #Solve for Sigma
    sig = solve( M, RHS )

    #Get next x iteration value
    x_new = x_old + sig

    #Get error
    res = x_new - x_old
    res_norm = norm(res)

    #if error is small enough, exit loop
    if res_norm < tol: break

    #assign next iteration value to previous value
    x_old = x_new

  #return root
  return x_new

#Perform Runge-Kutta method for an input butcher's table
def runge_kutte_allorder(a, b, c, f, dfdy, T, dt, y0, exact_sol_available=False, exact_sol=0):

  #initialize GTE
  gte = 0

  # calculate number of iterations
  n = int(T/float(dt) + 0.5)

  #Calculate order
  s = len(b)

  #initialize variables
  k_n = zeros(s)

  # Use initial condition:
  t_list = [0]
  y_list = [y0]

  # Calculate approximations for all time steps:
  for i in range(n):

    #find k values
    k_n = newton_multi(function, Jacobian, k_n, f, dfdy, t_list[i], y_list[i], dt, c, a)

    #initialize temp sum
    tempSum = 0

    #calculate weighted sum of kis
    for j in range(s):

      tempSum += b[j]*k_n[j]

    #calculate y and append
    y_n_plus_one = y_list[i] + dt * tempSum
    y_list.append(y_n_plus_one)

    #Calculate time and append
    t_n_plus_one = t_list[i] + dt
    t_list.append(t_n_plus_one)

    #Find GTE if exact solution available
    if exact_sol_available:
      err_n = abs(exact_sol(t_n_plus_one) - y_n_plus_one)
      if err_n > gte: gte = err_n

  # Return the solution and error:
  return[t_list,y_list, gte]

#generates F vector for a RK method, where Fi = ki-f(tn+ci*h,yn+sum(aij*ki)
def function( f, tn, yn, dt, c, a, k ):

  #find RK order
  s = len( c )

  #initialize function array
  result = zeros( s )

  #loop through each column of result
  for i in range( s ):

    #initialize temp sum
    tempSum = 0

    #calculate weighed sum of kjs
    for j in range( s ):

      tempSum += a[i][j]*k[j]

    #calculate each column of F
    result[i] = k[i] - f(tn + c[i]*dt, yn + dt*tempSum)

  #return F
  return result

#generates jacobian for dFj/dkis
def Jacobian( dfdy, tn, yn, dt, c, a, k ):

  #find RK order
  s = len( c )

  #initialize Jacobian
  result = zeros( [s,s] )

  #loop through each row of Jacobian(kis)
  for i in range( s ):

    #initialize temp sum
    tempSum = 0

    #calculate weighted sum of kjs
    for j in range( s ):

      tempSum += a[i][j]*k[j]

    #loop through columns of Jacobian(Fis)
    for j in range( s ):

      #calculate each element of J
      result[i][j] = kdel(i,j)-dfdy(tn + c[i]*dt, yn + dt*tempSum)*dt*a[i][j]

  #return J
  return result

#evaluates kronecker delta function for an i and j
def kdel( i, j ):

  #if i = j, result is 1
  if (i == j):
    return 1

  #if i != j, result is 0
  else:
    return 0

#Example butcher's tables
'''
a = [[0.,0.],[0.,1.]]

b = [0.,1.]

c=[0.0,0.5]
'''
'''
a = [[1.]]
b = [1.]
c = [0.]
'''
a = [[0.1666667,-0.3333333,0.1666667],[0.1666667,0.4166667,-0.0833333],[0.1666667,0.6666667,0.1666667]]
b = [0.1666667,0.6666666,0.1666667]
c = [0.,0.5,1.]

#input derivative functions and its derivative
def f(t,y):
  return y*((1/(t+1))-1)

def dfdy(t,y):
  return ((1/(t+1))-1)

#final time
T = 5.

#time step(h)
dt = 0.1

#inital condition
y0 = 1.

#set exact solution available flag to true
exact_sol_available =True

def exact_sol(t):
  return (t+1)*exp(-t)

#run RK method
[t_list,y_list,error] = runge_kutte_allorder(a, b, c, f, dfdy, T, dt, y0, exact_sol_available, exact_sol)

#plot Result
clf()
axis('equal')
label = "Runge-Kutta Results of order"
plot(t_list, y_list, "b-", label=label)
if exact_sol_available:
  label = "Exact Solution"
  plot(t_list,[exact_sol(t) for t in t_list], "r-", label = label)
grid(True)
legend()
lab.show()

#if exact solution available, generate and plot error vs timestep
if exact_sol_available:

  gte=[]

  time_steps = [0.5, 0.4, 0.3, 0.2, 0.1, 0.05, 0.025, 0.0125]
  for dt in time_steps:
    [t_list, y_list, error] = runge_kutte_allorder(a, b, c, f, dfdy, T, dt, y0, exact_sol_available, exact_sol)
    gte.append(error)

  # Plot the solution:
  clf()
  label = "GTE as a function of dt"
  axis('equal')
  loglog(time_steps, gte, "b-", label=label)
  grid(True)
  legend()
  lab.show()


#imports
import numpy as np
import matplotlib.pyplot as plt

def est_coeff(x,y):
	m=np.size(x); #number of trainingexamples

	mean_x,mean_y =  np.mean(x),np.mean(y) #mean

	ss_xy = np.sum(y*x) - m*mean_y*mean_x #cross deviation
	ss_xx = np.sum(x*x) - m*mean_x*mean_x

	b1 = ss_xy/ss_xx #regression coefficient
	b0 = mean_y - b1*mean_x

	return(b0,b1)

def plot_regressionline(x,y,b):
	plt.scatter(x, y) #original data

	y= b[0] + b[1]*x #predicted data

	plt.plot(x, y, color = "g") #regression line

	plt.xlabel('x') #labels
	plt.ylabel('y')

	plt.show()
def main():

	x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])  #training data
	y = np.array([2, 3, 5, 6, 8, 9, 12, 14, 15, 16])

	b = est_coeff(x, y) #estimating coefficients
	print("Estimated coefficients:\nb0 = {} \
		\nb1 = {}".format(b[0], b[1]))

	plot_regressionline(x, y, b) #plotting regression line

if __name__ == "__main__":
	main()


##########################################################################
## PROBABILITY DISTRIBUTIONS                                            ##
##########################################################################

#Import general packages
import math
import numpy as np
import scipy as sp
#import random as rd
import matplotlib.pyplot as plt

##import scikits.timeseries as ts
##import scikits.timeseries.lib.plotlib as tpl

#Import framework packages
##from FDC import *               #SVH-package
##from CRVC import *              #SVH-package
##from plot_functies import *     #SVH-package
##from obj_functies import *	    #SVH-package

def NormalizePDF(Input):
    '''
    Sum of all elements becomes 1
    '''
    Normed=Input/Input.sum()
    return Normed

def Normalize(Input):
    '''
    Normalization, values recalculated between 0 and 1
    '''
    Normed=(Input-min(Input))/(max(Input)-min(Input))
    return Normed

#########################################################################
# PDF   Get probability value for a certain input!                     ##
#########################################################################

def UniformDistribution(x,left=0.0,right=1.0,val=1.0):
    '''
    Probability is 1. when in the region
    '''
    if left<=x<=right:
        px=val
    else:
        px=0.0
    return px

def TriangularDistribution(x,left,mode,right):
    '''
    Calculates the "weight factor" of triangular, based on a certain inputvalue

    see numpy manual (or Beven_book: left=0, right=1)
    '''
    if mode>right:
        print 'right en mode zijn omgewisseld!!'
    if left<=x<=mode:
        px=2*(x-left)/((right-left)*(mode-left))
    elif mode<=x<=right:
        px=2*(right-x)/((right-left)*(right-mode))
    else:
        px=0.0
    return px

def TrapezoidalDistribution(x,left,mode1,mode2,right):
    '''
    Calculates the "weight factor" of trapezoidal
     based on a certain inputvalue
    '''
    if mode1>right:
        print 'right en mode1 zijn omgewisseld!!'
    if mode2>right:
        print 'right en mode2 zijn omgewisseld!!'
    if mode1>mode2:
        print 'mode1 en mode2 zijn omgewisseld!!'

    u=2/(right+mode2-mode1-left)

    if left<=x<=mode1:
        px=u*(x-left)/(mode1-left)
    elif mode1<=x<=mode2:
        px=u
    elif mode2<=x<=right:
        px=u*(right-x)/(right-mode2)
    else:
        px=0.0
    return px

def NormalDistribution(x, mu=0.0, sigma=1.0, wholePDF=True,left=None,right=None):
    if wholePDF == True:
        px=1*np.exp(- (x - mu)**2 / (2 * sigma**2)) /(sigma * np.sqrt(2 * np.pi))
    else:           #cut boundaries to use it as membership function
        if left<=x<=right:
            px=1*np.exp(- (x - mu)**2 / (2 * sigma**2)) /(sigma * np.sqrt(2 * np.pi))
        else:
            px=0.0
    return px

def LogNormalDistribution(x, mu=0.0, sigma=1.0, wholePDF=True,left=0.0,right=None):
    if wholePDF == True:
        px = np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2))/(x * sigma * np.sqrt(2 * np.pi))
    else:
        if left<=x<=right:
            px = np.exp(-(np.log(x) - mu)**2 / (2 * sigma**2))/(x * sigma * np.sqrt(2 * np.pi))
        else:
            px=0.0
    return px

##########################################################################
##RANDOM SAMPLING                                                       ##
##########################################################################

#Uniform
def randomUniform(left=0.0,right=1.0,rnsize=None):
    '''
    link to uniform sampling of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    left: float
        lower value
    right: float
        upper value
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.uniform
    '''
    rn=np.random.uniform(left,right,rnsize)
    return rn

#triangular
def randomTriangular(left=0.0, mode=None, right=1.0, rnsize=None):
    '''
    link to triangular sampling of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    left: float
        lower value
    mode: float
        value between left and right, highest probability
    right: float
        upper value
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.triangular
    '''

    if mode==None:
        print 'Triangular needs mode-value'
    rn=np.random.triangular(left, mode, right, rnsize)
    return rn

#trapezoidal
def randomTrapezoidal(left=0.0,mode1=None,mode2=None,right=1.0,rnsize=None):
    '''
    random sampling from trapezoidal function

    Parameters
    ----------
    left: float
        lower value
    mode1: float
        value between left and right, highest probability left side
    mode2: float
        value between left and right, highest probability right side
    right: float
        upper value
    rnsize: int
        number of samples

    '''
    if mode1==None:
        print 'Triangular needs 2 mode-values'
    if mode1==None:
        print 'Triangular needs 2 mode-values'

    rn=np.zeros(rnsize)
    for i in range(np.size(rn)):
        y = np.random.uniform(0.0,1.0,1)
        h=2/(right+mode2-mode1-left)

        a=left
        b=right
        c=mode1
        d=mode2

        if 0.0<=y<=(h*(c-a)/2):
            rn[i]=a+np.sqrt(2*(c-a)/h)*np.sqrt(y)
        elif (h*(c-a)/2)<=y<=(1-h*(b-d)/2):
            rn[i]=(a+c)/2 + y/h
        elif (1-h*(b-d)/2)<=y<=1.0:
            rn[i]=b-np.sqrt(2*(b-d)/h)*np.sqrt(1-y)
        else:
            print 'not in correct range'
    return rn

#Normal
def randomNormal(mu=0.0, sigma=1.0, rnsize=None):
    '''
    link to sampling of normal distribution of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    mu: float
        mean value
    sigma: float
        Standard deviation (spread or 'width') of the distribution
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.normal
    '''

    rn=np.random.normal(mu, sigma, rnsize)
    return rn

#lognormal
def randomLogNormal(mu=0.0, sigma=1.0, rnsize=None):
    '''
    link to sampling of lognormal distribution of numpy, to remain consistency in names
    of the pyFUSE module

    Parameters
    ----------
    mu: float
        Mean value of the underlying normal distribution
    sigma: float
        Standard deviation of the underlying normal distribution
    rnsize: int
        number of samples

    See Also
    ---------
    numpy.random.lognormal
    '''

    rn=np.random.lognormal(mu, sigma, rnsize)
    return rn

#distribution selector
def DistSelector(args,distname='randomUniform'):
    if distname =='randomUniform':
        return randomUniform(left=args[0],right=args[1],rnsize=args[2])
    elif distname =='randomTriangular':
        return randomTriangular(left=args[0], mode=args[1], right=args[2], rnsize=args[3])
    elif distname =='randomTrapezoidal':
        return randomTrapezoidal(left=args[0],mode1=args[1],mode2=args[2],right=args[3],rnsize=args[4])
    elif distname =='randomNormal':
        return randomNormal(mu=args[0], sigma=args[1], rnsize=args[2])
    elif distname =='randomLogNormal':
        return randomLogNormal(mu=args[0], sigma=args[0], rnsize=args[0])
    else:
        raise Exception('Wrong ditribution name; choose from: randomUniform, randomTriangular, randomTrapezoidal, randomNormal, randomLogNormal')

##########################################################################
##INVERSE DISTRIBUTIONS (from uniform to other distributions sampling)  ##
##########################################################################

def stnorm2norm(stn,mu,sigma):
    return sigma*stn + mu

def ltqnorm(p):
    """
    Modified from the author's original perl code (original comments follow below)
    by dfield@yahoo-inc.com.  May 3, 2004.

    Lower tail quantile for standard normal distribution function.

    This function returns an approximation of the inverse cumulative
    standard normal distribution function.  I.e., given P, it returns
    an approximation to the X satisfying P = Pr{Z <= X} where Z is a
    random variable from the standard normal distribution.

    The algorithm uses a minimax approximation by rational functions
    and the result has a relative error whose absolute value is less
    than 1.15e-9.

    Author:      Peter John Acklam
    Time-stamp:  2000-07-19 18:26:14
    E-mail:      pjacklam@online.no
    WWW URL:     http://home.online.no/~pjacklam
    """

    if p <= 0 or p >= 1:
        # The original perl code exits here, we'll throw an exception instead
        raise ValueError( "Argument to ltqnorm %f must be in open interval (0,1)" % p )

    # Coefficients in rational approximations.
    a = (-3.969683028665376e+01,  2.209460984245205e+02, \
         -2.759285104469687e+02,  1.383577518672690e+02, \
         -3.066479806614716e+01,  2.506628277459239e+00)
    b = (-5.447609879822406e+01,  1.615858368580409e+02, \
         -1.556989798598866e+02,  6.680131188771972e+01, \
         -1.328068155288572e+01 )
    c = (-7.784894002430293e-03, -3.223964580411365e-01, \
         -2.400758277161838e+00, -2.549732539343734e+00, \
          4.374664141464968e+00,  2.938163982698783e+00)
    d = ( 7.784695709041462e-03,  3.224671290700398e-01, \
          2.445134137142996e+00,  3.754408661907416e+00)

    # Define break-points.
    plow  = 0.02425
    phigh = 1 - plow

    # Rational approximation for lower region:
    if p < plow:
       q  = math.sqrt(-2*math.log(p))
       return (((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / \
               ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)

    # Rational approximation for upper region:
    if phigh < p:
       q  = math.sqrt(-2*math.log(1-p))
       return -(((((c[0]*q+c[1])*q+c[2])*q+c[3])*q+c[4])*q+c[5]) / \
                ((((d[0]*q+d[1])*q+d[2])*q+d[3])*q+1)

    # Rational approximation for central region:
    q = p - 0.5
    r = q*q
    return (((((a[0]*r+a[1])*r+a[2])*r+a[3])*r+a[4])*r+a[5])*q / \
           (((((b[0]*r+b[1])*r+b[2])*r+b[3])*r+b[4])*r+1)

def ltqnormarr(parr,mu=0.0,sigma=1.):
    stnorm=np.array([ltqnorm(p) for p in parr])
    print type(stnorm)
    return stnorm2norm(stnorm,mu,sigma)

##########################################################################
##TESTFIGUREN ETC                                                       ##
##########################################################################

#TEST
##import re
##plt.figure()
##trg='UniformDistribution'
##x=np.arange(-3.0,3.0,0.01)
##y=np.zeros(np.size(x))
##for i in range(np.size(x)):
##    #y[i]= TriangularDistribution(x[i],0.0,0.4,1.0)
##    #y[i]= UniformDistribution(x[i])
##    y[i]= eval(trg+'(x[i])')
##    #y[i]= NormalDistribution(x[i],wholePDF=True)
##    #y[i]= LogNormalDistribution(x[i],mu=0.5, sigma=1.0, wholePDF=False,right=2.8)
##ynorm=Normalize(y)
##plt.plot(x,ynorm,'ro')
##plt.show()
##plt.plot(x,y,'ro')
###plt.savefig('uniformmake.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')
##plt.savefig('lognormalmake.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')

##plt.figure()
##x=np.arange(-1.0,1.0,0.01)
##y=np.zeros(np.size(x))
##for i in range(np.size(x)):
##    y[i]= TriangularDistribution(x[i],-1.0,0.0,1.0)
##    #y[i]= UniformDistribution(x[i])
##    #y[i]= NormalDistribution(x[i],wholePDF=True)
##    #y[i]= LogNormalDistribution(x[i],mu=0.0, sigma=1.0, wholePDF=False,right=2.8)
##ynorm=Normalize(y)
###plt.plot(x,ynorm,'ro')
###y2=randomLogNormal(mu=0.0, sigma=1.0, rnsize=100000)
##y2=randomTriangular(left=-1.0, mode=0.0, right=1.0, rnsize=10000)
##plt.hist(y2, bins=200,normed=True, cumulative=False,color='b', edgecolor='b')
##plt.plot(x,y,'ro')
###plt.axis([-3, 3, 0, 1])
##plt.show()

##plt.figure()
##s = randomUniform(0.0,1.0,100000)
##count, bins, ignored = plt.hist(s, 200, normed=True)
##plt.plot(bins, np.ones_like(bins), linewidth=2, color='r')
##
##testnr=[1000000,100000,10000,1000,100,10]
##npt=[1,2,3,4,5,6]
##colors=['k','b','y','m','c','g']
##
##plt.figure()
##tg=0
##for i in testnr:
##    plt.subplot(3,2,npt[tg])
##    y=randomtrapezoidal(0.0,0.2,0.5,1.0,i)
##    h = plt.hist(y, bins=200,normed=True, cumulative=False,color=colors[tg], edgecolor=colors[tg])
##    plt.title(str(i)+ ' samples')
##    setp(gca(), xticklabels=[])
##    setp(gca(), yticklabels=[])
##    x=np.arange(0.0,1.0,0.0001)
##    y=np.zeros(np.size(x))
##    for i in range(np.size(x)):
##        y[i]= TrapezoidalDistribution(x[i],0.0,0.2,0.5,1.0)
##    plt.plot(x,y,'r')
##    setp(gca(), xticklabels=[])
##    setp(gca(), yticklabels=[])
##
##    tg=tg+1
##
##plt.savefig('nrMCTrap.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')
##plt.figure()
##tg=0
##for i in testnr:
##    plt.subplot(3,2,npt[tg])
##    s = np.random.uniform(0.0,1.0,i)
##    count, bins, ignored = plt.hist(s, 200, normed=True, color=colors[tg], edgecolor=colors[tg])
##    plt.plot(bins, np.ones_like(bins), linewidth=2)
##    plt.title(str(i)+ ' samples')
##    setp(gca(), xticklabels=[])
##    setp(gca(), yticklabels=[])
##    tg=tg+1
##
##plt.savefig('nrMCUnif.png', dpi=300, facecolor='w', edgecolor='w',orientation='portrait', papertype=None,transparent=False,bbox_inches='tight')


# -*- coding: utf-8 -*-
"""

Author: Chaofeng Wang

Logistic regression

Implemented gradient ascent by batch and mini batch version

very sensitive to outliers

"""
import numpy as np

class Logi_regression:

    def LoR_BGA(self,x,y,alpha):

        xsize = x.shape

        error = np.matlib.ones((xsize[0],1)) # initialize error vector

        coef = np.matlib.zeros((xsize[1]+1,1)) # initialize coefficients

        X = np.hstack((x,np.ones((xsize[0],1)))) # adding intercept

        maxCount = 2000

        count = 0

        while np.linalg.norm(error) > 0.001 and count < maxCount:

                count = count + 1

                error = y - (1 / (1 + np.exp(-X * coef)))

                coef = coef + alpha * X.T  * error

        return coef

    def LoR_SGA(self,x,y,alpha):
        xsize = x.shape

        error = np.matlib.ones((xsize[1],1)) # initialize error vector

        coef = np.matlib.zeros((xsize[1]+1,1)) # initialize coefficients

        X = np.hstack((x,np.ones((xsize[0],1)))) # adding intercept

        maxCount = 2000

        count = 0

        B = 2 # mini-batch size

        index_total = range(xsize[0])

        while np.linalg.norm(error) > 0.001 and count < maxCount:

                index = np.random.choice(index_total,B,replace = False) # random choose index

                y_batch = y[index,:]

                X_batch = X[index,:]

                count = count + 1

                error = y_batch - (1 / (1 + np.exp(-X_batch * coef)))

                coef = coef + alpha * X_batch.T  * error

        return coef

if __name__ == "__main__":

    a = Logi_regression()
    ### input data as x,y
    x = np.matrix(x)
    y = np.matrix(y)

    x = x.T # column vector
    y = y.T #column vector

    alpha = 0.001 # learning rate

    coef_BGA = a.LoR_BGA(x,y,alpha)
    coef_SGA = a.LoR_SGA(x,y,alpha)

    xsize = x.shape
    X = np.hstack((x,np.ones((xsize[0],1))))

    r_BGA_err =  y- (1 / (1 + np.exp(-X * coef_BGA)))
    r_SGA_err =  y- (1 / (1 + np.exp(-X * coef_SGA)))


import numpy as np

class Linear:
    def __init__(self, X, y):
        self.X = np.concatenate((np.ones(y.shape), X), axis = 1)
        self.y = y
        self.preds = np.zeros(y.shape)
        self.n = y.shape[0]

        self.loss = None
        self.lr = 0.001 # funny, it the lr is too big I don't get convergence
        self.beta = np.random.uniform(low=-1.0, high=1.0, size= (self.X.shape[1], 1))
        self.grad = None

    def forward(self):
            self.preds = np.dot(self.X, self.beta)

    def backward(self):
        self.grad =2*  np.mean((self.preds - self.y)* self.X,
        axis=0, keepdims=True ).T

        self.beta = self.beta -  self.lr * self.grad

    def calculate_loss(self, y, preds):
        return np.mean( (y - preds)**2 )

    def fit(self, iters):
        for _ in range(iters):
            self.forward()
            self.backward()
            self.loss = self.calculate_loss(self.y, self.preds)

    def fit_exact(self):
        self.beta_exact = np.linalg.solve(np.dot(self.X.T,self.X),
        np.dot(self.X.T,self.y))

    def pred(self, X):
        return np.dot(X, self.beta)

    def pred_exact(self, X):
        return np.dot(X, self.beta_exact)

if __name__ == '__main__':
    from data_prep import Data
    data= Data()

    data.load_data('./data/USA_Housing.csv', 'Price')

    data.normalize(norm_target = True)
    X_train, X_test, y_train, y_test =  data.split_data(data.X_norm, data.y_norm)

    linear= Linear(X_train, y_train)
    linear.fit(iters=10000)
    linear.fit_exact()

    print('Beta from gradient descent:\n{}'.format(linear.beta))
    print('Beta from matrix inversion:\n{}'.format(linear.beta_exact))

    print('Training MSE (Gradient descent):{}'.format(linear.loss))

    # Need to make uniform convention for bias matrix
    X_test = np.concatenate((np.ones(y_test.shape), X_test), axis = 1)

    print('Test MSE (Gradient descent):{}'.format(
    linear.calculate_loss(y_test, linear.pred(X_test))
    ))
    print('Test MSE (Matrix inversion):{}'.format(
    linear.calculate_loss(y_test, linear.pred_exact(X_test))
    ))


import numpy as np
import sympy as sp

def jacobianMatrix(func1, func2, points):
	res = np.zeros((2,2))
	x = sp.Symbol('x')
	y = sp.Symbol('y')

	dx11 = sp.diff(func1,x)
	dx12 = sp.diff(func1,y)
	dx21 = sp.diff(func1,x)
	dx22 = sp.diff(func2,y)

	jx11 = lambda x, y : eval(str(dx11))
	jx12 = lambda x, y : eval(str(dx12))
	jx21 = lambda x, y : eval(str(dx21))
	jx22 = lambda x, y : eval(str(dx22))

	res[0,0] = jx11(points[0], points[1])
	res[0,1] = jx12(points[0], points[1])
	res[1,0] = jx21(points[0], points[1])
	res[1,1] = jx22(points[0], points[1])

	return res;

def fxMatrix(func1, func2, points):
	res = np.zeros((2,1))

	fx1 = lambda x, y : eval(str(func1))
	fx2 = lambda x, y : eval(str(func2))

	res[0,0] = fx1(points[0], points[1])
	res[1,0] = fx2(points[0], points[1])

	return res

def calculaNewton(request):
	graph  = []
    xl     = float(request.POST.get('xl'))       # Limite inferior
    xu     = float(request.POST.get('xu'))       # Limite superior
    TOL    = float(request.POST.get('tol'))      # Tolerância do cáculo
    N      = float(request.POST.get('maxi'))     # Maximo de iterações
    f1     = request.POST.get('func1')		     # Função a ser utilizada nos calculos
    f2     = request.POST.get('func2')		     # Função a ser utilizada nos calculos

	x = np.array([xl,xu])

	k=0
	# iteracoes
	while (k < N):
	   k += 1
	   #iteracao Newton
	   resultJacobian = jacobianMatrix(f1, f2, x)
	   resultFx = fxMatrix(f1, f2, x)

	   delta = -np.linalg.inv(resultJacobian).dot(resultFx)

	   x[0] = x[0] + delta[0];
	   x[1] = x[1] + delta[1];
	   graph.append([x[0], x[1]])

	   # #criterio de parada
	   if (np.linalg.norm(delta,np.inf) < TOL):
	       break

	return HttpResponse(json.dumps({ 'points': graph }), content_type="application/json")


# https://stephens999.github.io/fiveMinuteStats/likelihood_function.html
import numpy as np
import matplotlib.pyplot as plt

q = np.linspace(0,1, 100)

def likelihood_fn(x):
    return np.power(x, 30) * np.power((1-x), 70)

l_val = likelihood_fn(q)
# print(q)
# print(l_val)
max_index = np.argmax(l_val)

textstr = '\n'.join(('L_max: {0:.3e}'.format(l_val[max_index]),
                     'q: {0:.4f}'.format(q[max_index])))
print(textstr)

if True:
    props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
    plt.plot(q, l_val)
    plt.axvline(x=q[max_index], color='r', ls='--')
    plt.text(q[max_index]+0.05, l_val[max_index], textstr, fontsize=12, verticalalignment='top', bbox=props)
    plt.xlabel('q (probability)')
    plt.ylabel('likelihood')
    plt.show()

# normalization
l_val_normalized = l_val / l_val[max_index]
textstr += '\nnorm: {} (of course!)'.format(l_val_normalized[max_index])

if True:
    plt.plot(q, l_val_normalized)
    plt.axvline(x=q[max_index], color='r', ls='--')
    plt.text(q[max_index]+0.05, l_val_normalized[max_index], textstr, fontsize=12, verticalalignment='top', bbox=props)
    plt.xlabel('q (probability)')
    plt.ylabel('L(q)/L(q_hat)')
    plt.show()

def log_likelihood_fn(x):
    return 30*np.log(x) + 70 * np.log(1-x)

log_l_val = log_likelihood_fn(q)
print(log_l_val)
# print(np.argmax(log_l_val), log_l_val[np.argmax(log_l_val)])
log_l_val_normalized = log_l_val - log_likelihood_fn(0.3)
if True:
    plt.plot(q, log_l_val_normalized)
    plt.xlabel('q')
    plt.ylabel('log L(q) / log L(q_hat)')
    plt.ylim(-10,0)
    plt.show()


'''
@author: Hsinglu Liu
@version: 1.0
@Date: 2020.8.5
'''

from gurobipy import *
import pandas as pd
import numpy as np
from pandas import Series, DataFrame
import math

Arcs = {'1,2': [15, 15]    # cost flow
        ,'1,4': [25, 25]
        ,'1,3': [45, 45]
        ,'2,5': [30, 60]
        ,'2,4': [2, 2]
        ,'5,7': [2, 2]
        ,'4,7': [50, 100]
        ,'4,3': [2, 2]
        ,'3,6': [25, 50]
        ,'6,7': [1, 1]
       }
Arcs

Nodes = [1, 2, 3, 4, 5, 6, 7]

commodity = [[1, 7, 25],  # s_i, d_i, demand
             [2, 6, 2]
]

model = Model('MultiCommodity')

# add variables
X = {}
for key in Arcs.keys():
    for k in range(len(commodity)):
        key_x = key + ',' + str(k)
        X[key_x] = model.addVar(lb=0
                                ,ub=Arcs[key][1]
                                ,vtype=GRB.CONTINUOUS
                                ,name= 'x_' + key_x
                               )
# add objective function
obj = LinExpr(0)
for key in Arcs.keys():
    for k in range(len(commodity)):
        key_x = key + ',' + str(k)
        obj.addTerms(Arcs[key][0], X[key_x])
model.setObjective(obj, GRB.MINIMIZE)

# constraints 1
for k in range(len(commodity)):
    for i in Nodes:
        lhs = LinExpr(0)
        for key_x in X.keys():
#             nodes = key_x.split(',')
            if(i == (int)(key_x.split(',')[0]) and k == (int)(key_x.split(',')[2])):
                lhs.addTerms(1, X[key_x])
            if(i == (int)(key_x.split(',')[1]) and k == (int)(key_x.split(',')[2])):
                lhs.addTerms(-1, X[key_x])
        if(i == commodity[k][0]):
            model.addConstr(lhs == commodity[k][2], name='org_, ' + str(i) + '_' + str(k))
        elif(i == commodity[k][1]):
            model.addConstr(lhs == -commodity[k][2], name='des_, ' + str(i) + '_' + str(k))
        else:
            model.addConstr(lhs == 0, name='inter_, ' + str(i) + '_' + str(k))

# constraints 2
for key in Arcs.keys():
    lhs = LinExpr(0)
    for k in range(len(commodity)):
        key_x = key + ',' + str(k)
        lhs.addTerms(1, X[key_x])
    model.addConstr(lhs <= Arcs[key][1], name = 'capacity_, ' + key)

model.write('Multicommodity_model.lp')
model.optimize()

for var in model.getVars():
    if(var.x > 0):
        print(var.varName, '\t', var.x)
dual = model.getAttr("Pi", model.getConstrs())


import numpy as np

class KittiProjector:
    """
    class to project kitti velodyne points
    """

    ## Spherical Projection ###############################################################

    @staticmethod
    def cartesian_to_spherical(x: np.array, y: np.array, z: np.array) -> [np.array, np.array, np.array]:
        """
        function transforms cartesian coordinates into spherical ones

        Args:
            x: x axis values
            y: y axis values
            z: z axis values

        returns:
            spherical coordinate of every point in degrees

        """

        depth = np.sqrt(x ** 2 + y ** 2 + z ** 2)

        azimuth = np.arctan2(y, x)
        elevation = np.arctan2(z, depth)

        # transforms to degrees
        azimuth = np.rad2deg(azimuth)
        elevation = np.rad2deg(elevation)

        return azimuth, elevation, depth

    @staticmethod
    def get_pgm_index(
                    lidar_data: np.array,
                    pgm_height: int = 64,
                    pgm_width: int = 512,
                    vertical_field_view: tuple = (-25, 3),
                    horizontal_field_view: tuple = (-90, 90),
                    invert_z_axis=True) -> np.array:
        """
        function get polar grid map coordinates for every point in lidar scan
        Args:
            lidar_data: numpy array of lidar scan
            pgm_height: height of polar grid map
            pgm_width: width of polar grid map
            vertical_field_view:  tuple
                vertical field of view of lidar, angle of highest layer, angle of lowest layer
                for sick: (-1.5,1.5)
                for velodyne 64: (-25, 3)
            horizontal_field_view: horizontal field of view of layer, angles of extrem laser beans
                for sick (-42.5, 42.5)
                for velodyne 64: (-180, 180), for kitti (-90, 90)
            invert_z_axis: boolean, flag wether to invert z axis

        return:
        pgm_azimuth_idx, pgm_elevation_idx, depth: angles for every lidar point and its depth
        """

        # extract coordinate values
        x, y, z = lidar_data[:, 0], lidar_data[:, 1], lidar_data[:, 2]

        if invert_z_axis:
            z = -1 * z

        # get spherical coordinates of points
        azimuth, elevation, depth = KittiProjector.cartesian_to_spherical(x, y, z)

        vertical_step = (abs(vertical_field_view[0]) + abs(vertical_field_view[1])) / (pgm_height-1)
        horizontal_step = (abs(horizontal_field_view[0]) + abs(horizontal_field_view[1])) / (pgm_width-1)

        vertical_range = np.arange(vertical_field_view[0], vertical_field_view[1], vertical_step)
        horizontal_range = np.arange(horizontal_field_view[0], horizontal_field_view[1], horizontal_step)

        elevation_diff = np.abs(elevation.reshape(-1, 1) - vertical_range.reshape(1, -1))
        azimuth_diff = np.abs(azimuth.reshape(-1, 1) - horizontal_range.reshape(1, -1))

        pgm_azimuth_idx = np.argmin(azimuth_diff, axis=1).squeeze()
        pgm_elevation_idx = np.argmin(elevation_diff, axis=1).squeeze()

        pgm_azimuth_angles = horizontal_range[pgm_azimuth_idx]
        pgm_elevation_angles = vertical_range[pgm_elevation_idx]

        return depth, pgm_azimuth_angles, pgm_elevation_angles

   ## project points to image coordinates #############################################################

    @staticmethod
    def project_velo2cam(lidar_data: np.array, p_rect_02_matrix: np.array, r_rect_00_matrix: np.array, tr_velo_matrix: np.array) -> np.array:
        """
        function project lidar points into image
        :param
            lidar_data: lidar points to be projected, shape: number_of_points,3 (-1,x,y,z)
            p_rect_02_matrix: projection matrix
            r_rect_00_matrix: rectifying rotation matrix of cam 0 in homogeneous space
            tr_velo_matrix:   translation rotation matrix of cam 0 in homogeneous space
        :return:
            projection_points_normalized: projected points in pixel coordinates: u,v
        """

        # transform lidar points to homogeneous space (x,y,z,1)
        velo_points_homogenous = np.concatenate([lidar_data, np.ones((lidar_data.shape[0], 1))], axis=1)

        # calculate projection matrix:
        projection_matrix = np.dot(p_rect_02_matrix, np.dot(r_rect_00_matrix, tr_velo_matrix))

        # project points [ x,y,z,1] --> [x,y,s]
        projection_points = np.dot(projection_matrix, velo_points_homogenous.T)

        # normalize projected points [x,y,s] --> [x/s,y/s] = [u,v]
        projection_points_normalized = projection_points[:2, :] / projection_points[2, :]

        return projection_points_normalized.T


"""Kernel function

LinearKernel, GaussianKernel, SigmoidKerne, RBFKernel, ExponentialKernel, GramMatrix are implemented

"""

import numpy as np
from abc import ABC,abstractclassmethod

class BaseKernel(ABC):
    def __init__(self):
        pass

    @abstractclassmethod
    def __call__(self,x,y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y)

        """
        pass

class LinearKernel(BaseKernel):
    """Linear Kernel

    Linear Kernel : k(x,y) = x^Ty

    """
    def __init__(self):
        super(LinearKernel,self).__init__()

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = x^Ty

        """
        return np.dot(x,y)

class GaussianKernel(BaseKernel):
    """Gaussian Kernel

    Gaussian Kernel : k(x,y) = exp(-|x-y|^2/2sigma^2)

    """
    def __init__(self,sigma=0.1):
        """

        Args:
            sigma (float) : parameter for kernel function

        """
        super(GaussianKernel,self).__init__()
        self.sigma = sigma

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = exp(-|x-y|^2/2sigma^2)

        """
        return np.exp(-np.dot(x-y,x-y)/(2*self.sigma**2))

class SigmoidKernel(BaseKernel):
    """Sigmoid Kernel

    Sigmoid Kernel: k(x,y) = tanh(ax^Ty + b)

    """
    def __init__(self,a=1,b=0):
        """

        Args:
            a,b (float) : parameter for kernel function

        """
        super(SigmoidKernel,self).__init__()
        self.a = a
        self.b = b

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = tanh(ax^Ty + b)

        """
        return np.tanh(self.a*np.dot(x,y) + self.b)

class RBFKernel(BaseKernel):
    """RBF Kernel

    RBF Kernel : k(x,y) = h(|x - y|)

    """
    def __init__(self,h):
        """

        Args:
            h (function) : k(x,y) = h(|x-y|)

        """
        super(RBFKernel,self).__init__()
        self.h = h

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = h(|x - y|)

        """
        return self.h(np.dot(x-y,x-y)**0.5)

class ExponentialKernel(BaseKernel):
    """Exponential Kernel

    Exponential Kernel : k(x,y) = exp(-theta|x - y|)

    """
    def __init__(self,theta=1):
        """

        Args:
            theta (float) : parameter for kernel function

        """
        super(ExponentialKernel,self).__init__()
        self.theta = theta

    def __call__(self, x, y):
        """

        Args:
            x,y (1-D array) : calculate k(x,y)

        Returns:
            float: k(x,y) = exp(-theta|x - y|)

        """
        return np.exp(-self.theta*np.dot(x-y,x-y)**0.5)

class GramMatrix():
    """Gram Matrix

    For making gram matrix.
    kernel type is
        LinearKernel
        GaussianKernel
        SigmoidKernel
        RBFKernel
        ExponentialKernel

    Attributes:
        kernel_func (function) : kernel_function

    """
    def __init__(self,kernel="Linear",sigma=0.1,a=1.0,b=0.0,h=None,theta=1.0):
        """

        Args:
            kernel (string) : kernel type (default "Linear"). you can choose "Linear","Gaussian","Sigmoid","RBF","Exponential"
            sigma (float) : for "Gaussian" kernel
            a,b (float) : for "Sigmoid" kernel
            h (function) : for "RBF" kernel
            theta (float) : for "Exponential" kernel

        """
        self.kernel_func = None
        if kernel == "Linear":
            self.kernel_func = LinearKernel()
        elif kernel == "Gaussian":
            self.kernel_func = GaussianKernel(sigma=sigma)
        elif kernel == "Sigmoid":
            self.kernel_func = SigmoidKernel(a=a,b=b)
        elif kernel == "RBF":
            if h is None:
                raise ValueError("if kernel is 'RBF', h must not be None.")
            self.kernel_func = RBFKernel(h=h)
        elif kernel == "Exponential":
            self.kernel_func = ExponentialKernel(theta=theta)
        else:
            raise ValueError(f"kernel '{kernel}' is inappropriate")

    def __call__(self,X):
        """

        Args:
            X (2-D array) : shape = (N_samples,N_dims)

        Returns:
            2-D array: Gram matrix, shape = (N_samples,N_samples)

        Note:
            time complexity is O(N_samples^2*N_dims)

        """
        N = X.shape[0]
        G = np.zeros((N,N))
        for i in range(N):
            G[i] = np.array([self.kernel_func(X[i],X[j]) for j in range(N)])
        return G


import numpy as np

def COD(x, y, theta):
    mean_of_ys = np.sum(y) / len(y)
    y_variation = np.sum(np.square(y - mean_of_ys))

    test_hypothesis = np.dot(x, theta)
    sq_error = np.sum(np.square(test_hypothesis - y))
    r2 = 1 - (sq_error / y_variation)
    return r2

def adj_r2(num_of_params, num_of_feats, r2):
    adj_r2 = 1 - (1 - r2) * (num_of_feats - 1) / \
        (num_of_feats - num_of_params - 1)
    return adj_r2

def ZNorm(x):
    mi = np.mean(x, axis=0)
    sigma = np.std(x, axis=0)
    x_norm = (x - mi) / sigma
    return x_norm, mi, sigma

def computeCost(y, hypothesis):
    train_len = len(y)
    J = 1 / (2 * train_len) * np.sum(np.square(hypothesis - y))
    return J

def gradientDescent(x_train, x_test, y_train, y_test, theta, alpha, num_iters, alpha_increase):
    train_len = len(y_train)
    test_lenn = len(y_test)
    history_train = np.zeros(num_iters)
    history_test = np.zeros(num_iters)

    for i in range(num_iters):
        # train
        hypothesis = np.dot(x_train, theta)
        history_train[i] = computeCost(y_train, hypothesis)
        theta -= alpha * (1 / train_len) * np.dot(x_train.T, (hypothesis - y_train))
        # test
        hypothesis = np.dot(x_test, theta)
        history_test[i] = computeCost(y_test, hypothesis)

        if i % alpha_increase == 0 and i != 0:
            alpha *= .1
            alpha_increase = i * 10

    return theta, history_train, history_test


import numpy as np

a = np.array([3, 4])
np.linalg.norm(a)

b = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])
c = np.array([1, 0, 1])

# 矩阵和向量之间的乘法
np.dot(b, c)            		# array([ 4, 10, 16])
np.dot(c, b.T)          		# array([ 4, 10, 16])

np.trace(b)             		# 求矩阵的迹，15
np.linalg.det(b)        		# 求矩阵的行列式值，0
np.linalg.matrix_rank(b)	# 求矩阵的秩，2，不满秩，因为行与行之间等差

d = np.array([
    [2, 1],
    [1, 2]
])

'''
对正定矩阵求本征值和本征向量
本征值为u，array([ 3.,  1.])
本征向量构成的二维array为v，
array([[ 0.70710678, -0.70710678],
       [ 0.70710678,  0.70710678]])
是沿着45°方向
eig()是一般情况的本征值分解，对于更常见的对称实数矩阵，
eigh()更快且更稳定，不过输出的值的顺序和eig()是相反的
'''
u, v = np.linalg.eig(d)

# Cholesky分解并重建
l = np.linalg.cholesky(d)

'''
array([[ 2.,  1.],
       [ 1.,  2.]])
'''
np.dot(l, l.T)

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

# 对不镇定矩阵，进行SVD分解并重建
U, s, V = np.linalg.svd(e)

S = np.array([
    [s[0], 0],
    [0, s[1]]
])

'''
array([[ 1.,  2.],
       [ 3.,  4.]])
'''
np.dot(U, np.dot(S, V))


import numpy as np

def MSE(x, x_hat):
    mse = np.sum((x - x_hat)**2) / len(x)
    return mse

def MRAE(x, x_hat):
    # Mean relative absolute error
    mrae = np.sum(np.abs((x - x_hat) / x)) / len(x)
    return mrae

def GFC(x, x_hat):
    # Goodness of fit coefficient
    x_normed = x / np.sqrt(np.sum(x**2))
    x_hat_normed = x_hat / np.sqrt(np.sum(x_hat**2))
    gfc = np.dot(x_normed, x_hat_normed)
    return gfc

def PSNR(v_max, x, x_hat):
    return 20 * np.log10(v_max / MRAE(x, x_hat))

if __name__ == '__main__':
    x = np.array([-1, 2, 3, 4, 5, 6, 7, 8])
    x_hat = np.array([-1.1, 2, 3.1, 4, 4.5, 7, 7.1, 8])
    print(MRAE(x, x_hat), MRAE(x * 10, x_hat * 10))
    print(GFC(x, x_hat), GFC(x * 10, x_hat * 10))


#!/usr/bin/python3

"""
    By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.

What is the 10001st prime number?
"""

import numpy as np

def isprime(num: int) -> bool:
    for i in range(2, int(np.sqrt(num))+1):
        if num%i == 0:
            return False
    return True

def euler7() -> int:
    tot = 1
    i = 3
    while True:
        if isprime(i):
            tot = tot + 1
            if tot == 10001:
                break
        i = i + 2
    return i

tot = euler7()
print(tot)


import numpy as np
import math

def logistic_sigmoid(x):
	return 1.0 / (1.0 + np.math.exp(-x))

def logistic_sigmoid_derivative(x):
	z = logistic_sigmoid(x)
	return z * (1.0 - z)

def normal_distribution(mu, sigma_squared, x):
	return 1.0/(math.sqrt(2*math.pi*sigma_squared))*math.exp(-(x-mu)**2/(2*sigma_squared))

def one_1_at(size, index):
	res = np.zeros(size)
	res[index] = 1.0
	return res


from math import cos, sin
import numpy as np

def calculate_rotation_matrix_extrinsic(alpha_rad, beta_rad, gamma_rad):
    """
    Applies extrinsic rotation defined by Euler angles alpha, beta, gamma.
    Counter-clockwise transformation for angles is used.

    Rotational matrix is a multiplication of the basic rotations over one axis.

    R = Rz(gamma)*Ry(beta)*Rx(alpha)

    where basic rotations are:

                 1    0        0
    Rx(alpha) =  0 cos(alpha) -sin(alpha)
                 0 sin(alpha)  cos(alpha)

                cos(beta)    0  sin(beta)
    Ry(beta) =     0         1     0
                -sin(beta)   0  cos(beta)

                cos(gamma) -sin(gamma)  0
    Rz(gamma) = sin(gamma)  cos(gamma)  0
                0              0        1

    The order of angles transformations is the very important,
    pay attention here we first transform gamma around z,
    then beta around y and then alpha around x.

    :param
    alpha: rotation around x axis (radians)
    beta:  rotation around y axis (radians)
    gamma: rotation around z axis (radians)
    :return:
    3x3 rotational matrix
    """

    Rx = [[1,    0,             0    ],
          [0, cos(alpha_rad), -sin(alpha_rad)],
          [0, sin(alpha_rad),  cos(alpha_rad)]]

    Ry = [[cos(beta_rad),  0, sin(beta_rad) ],
          [    0,          1,      0        ],
          [-sin(beta_rad), 0, cos(beta_rad)]]

    Rz = [[cos(gamma_rad), -sin(gamma_rad), 0],
          [sin(gamma_rad),  cos(gamma_rad), 0],
          [    0,                  0,       1]]

    R = np.dot(np.dot(Rz, Ry), Rx)

    return R


import numpy as np

def MAPE(y_real, y_predicted, aggregate):
	'''
	MAPE: mean absolute percentage error
	'''
	error = y_real - y_predicted
	absolute_error = np.abs(error)

	percentage_error = 100*absolute_error/y_real

	MAPE = aggregate(percentage_error)
	average_deviation = np.std(percentage_error)

	return MAPE, average_deviation

def MASE(y_real, y_predicted, r=14):
	'''
	MASE: Mean absoulte scaled error. r is the seasonality
	'''
	error = y_real - y_predicted
	absolute_error = np.abs(error)
	sum_absolute_error = np.sum(absolute_error)

	real_minus_r = np.zeros(len(y_real))
	real_minus_r[r:] = y_real[0:-r]

	sum_scaled = np.sum(np.abs(y_real - real_minus_r))

	return sum_absolute_error/sum_scaled

def MSE(y_real, y_predicted):
	'''
	MSE: Mean squared error
	'''
	error = y_real - y_predicted
	sse = np.dot(error, error)

	T = len(y_real)

	return sse/T


import csv
from math import sqrt
import numpy as np
import sys
import utils

def kNN(dataset, new_data, dfn, c_index=-1, k=1):
    dist = []
    for data in dataset:
        if c_index == -1:
            without_class = data[:-1]
        else:
            without_class = data[0:c_index] + data[(c_index+1):]
        dist.append((dfn(without_class, new_data), data[c_index]))

    dist.sort(key=lambda tup: tup[0])

    scoreboard = {}

    for vote in dist[:k]:
        if vote[1] not in scoreboard:
            scoreboard[vote[1]] = 0
        scoreboard[vote[1]] += 1

    winner = (0, None)
    for key, value in scoreboard.items():
        if value > winner[0]:
            winner = (value, key)

    return winner[1]

def hamming_dist(v1, v2):
    return np.sum(abs(np.array(v1) - np.array(v2)))

def euclidian_dist(v1, v2):
    return np.sqrt(np.sum((np.array(v1) - np.array(v2)) ** 2))

# def hamming_dist(v1, v2):
#     dist = 0
#     for i in range(len(v1)):
#         dist += abs(v1[i] - v2[i])
#     return dist

# def euclidian_dist(v1, v2):
#     dist = 0
#     for i in range(len(v1)):
#         dist += (v1[i] - v2[i]) ** 2
#     return np.sqrt(dist)

def linear_mahalanobis(training_set, pixels, callback):
    r = []
    g = []
    b = []
    r_sum = 0
    g_sum = 0
    b_sum = 0

    for position in training_set:
        (red, green, blue) = training_set[position]
        r.append(red)
        g.append(green)
        b.append(blue)

        r_sum += red
        g_sum += green
        b_sum += blue

    size = len(training_set)
    center = (r_sum / size, g_sum / size, b_sum / size)
    A = np.cov(np.array([r, g, b]))
    try:
        Ainv = np.linalg.inv(A)
    except:
        print("Error: failed to invert the pixel matrix")
        sys.exit(0)

    distance_map = {}
    for position in pixels:
        pixel_color = pixels[position]
        delta = utils.tuple_difference(pixel_color, center)
        deltaT = np.array([delta]).transpose()
        result = np.dot([delta], Ainv)
        result = np.dot(result, deltaT)
        distance = sqrt(result[0][0])
        distance_map[position] = distance

    max_dist = distance_map[max(distance_map)]
    for position in distance_map:
        distance = 255 * (1 - distance_map[position] / max_dist)
        callback(position, pixels[position], distance)

def quadratic_mahalanobis(training_set, pixels, callback):
    r = []
    g = []
    b = []
    r2 = []
    g2 = []
    b2 = []
    rg = []
    rb = []
    gb = []
    r_sum = 0
    g_sum = 0
    b_sum = 0
    r2_sum = 0
    g2_sum = 0
    b2_sum = 0
    rg_sum = 0
    rb_sum = 0
    gb_sum = 0

    for position in training_set:
        (red, green, blue) = training_set[position]
        r.append(red)
        g.append(green)
        b.append(blue)
        r2.append(red * red)
        g2.append(green * green)
        b2.append(blue * blue)
        rg.append(red * green)
        rb.append(red * blue)
        gb.append(green * blue)

        r_sum += r[-1]
        g_sum += g[-1]
        b_sum += b[-1]
        r2_sum += r2[-1]
        g2_sum += g2[-1]
        b2_sum += b2[-1]
        rg_sum += rg[-1]
        rb_sum += rb[-1]
        gb_sum += gb[-1]

    size = len(training_set)
    center = (r_sum / size, g_sum / size, b_sum / size,\
              r2_sum / size, g2_sum / size, b2_sum / size,\
              rg_sum / size, rb_sum / size, gb_sum / size)
    A = np.cov(np.array([r, g, b, r2, g2, b2, rg, rb, gb]))
    try:
        Ainv = np.linalg.inv(A)
    except:
        print("Error: failed to invert the pixel matrix")
        sys.exit(0)

    distance_map = {}
    for position in pixels:
        (r, g, b) = pixels[position]
        pixel = (r, g, b, r * r, g * g, b * b, r * g, r * b, g * b)
        delta = utils.tuple_difference(pixel, center)
        deltaT = np.array([delta]).transpose()
        result = np.dot([delta], Ainv)
        result = np.dot(result, deltaT)
        distance = sqrt(result[0][0])
        distance_map[position] = distance

    max_dist = distance_map[max(distance_map)]
    for position in distance_map:
        distance = 255 * (1 - distance_map[position] / max_dist)
        callback(position, pixels[position], distance)

def l2norm(training_set, pixels, callback):
    r_sum = 0
    g_sum = 0
    b_sum = 0

    for position in training_set:
        (red, green, blue) = training_set[position]
        r_sum += red
        g_sum += green
        b_sum += blue

    size = len(training_set)
    center = (r_sum / size, g_sum / size, b_sum / size)

    distance_map = {}
    for position in pixels:
        pixel_color = pixels[position]
        distance = euclidian_dist(pixel_color, center)
        distance_map[position] = distance

    max_dist = distance_map[max(distance_map)]
    for position in distance_map:
        distance = 255 * (1 - distance_map[position] / max_dist)
        callback(position, pixels[position], distance)


import pulp as pl
import numpy as np
# create model and solver
model = pl.LpProblem("1-1tiny-problem", pl.LpMinimize)

month_num = 8
product_num = 1

# parameters
q = [5000]  # product fix cost
p = [100]  # cost per product
d = np.array([400, 400, 800, 800, 1200, 1200, 1200, 1200]).reshape(
    (1, -1))  # demand of product by month
s_init = [200]  # initial stock
h = [5]  # stock cost per product month

# variables
x = pl.LpVariable.dicts('x', (range(product_num), range(month_num)), 0)
s = pl.LpVariable.dicts('s', (range(product_num), range(month_num)), 0)
y = pl.LpVariable.dicts('y', (range(product_num), range(month_num)),
                        cat='Binary')

# objective
model += pl.lpSum(pl.lpSum(p[i]*x[i][j] + q[i]*y[i][j] \
            for i in range(product_num)) for j in range(month_num)) \
            + pl.lpSum(pl.lpSum(h[i]*s[i][j] for i in range(product_num)) \
             for j in range(month_num)) \
            + pl.lpSum(h[i]*s[i][month_num-1]/2 for i in range(product_num))

# constraints
for i in range(product_num):
    # init stock
    model += s_init[i] + x[i][0] == d[i][0] + s[i][0]
    # first month max limit of x
    model += x[i][0] <= pl.lpSum(d[i][k]
                                 for k in range(0, month_num)) * y[i][0]
    for j in range(1, month_num):
        # stock balance
        model += s[i][j - 1] + x[i][j] == d[i][j] + s[i][j]
        # max limit of x
        model += x[i][j] <= pl.lpSum(d[i][k]
                                     for k in range(j, month_num)) * y[i][j]

# solver = pl.PULP_CBC_CMD()
# solver = pl.GUROBI_CMD(path=r"C:\gurobi911\win64\bin\gurobi_cl.exe")
solver = pl.SCIP_CMD(path=r"C:\Program Files\SCIPOptSuite 7.0.2\bin\scip.exe")
# solver = pl.GLPK_CMD(path=r"C:\Personal\Binary\glpk465\glpsol.exe")
# solver = pl.MOSEK()
# solver = pl.GUROBI()
result = model.solve(solver)
print("x val is")
for i in range(product_num):
    for j in range(month_num):
        print(pl.value(x[i][j]), end=",")
    print("\n")


import numpy as np

def _legDVR(start,stop,num):
    """
    Calculate Legendre DVR grid and operators

    Parameters
    ----------
    start : float
        Grid start value
    stop : float
        Grid stop value. Must be larger than start
    num : int
        Number of grid points. Must be >= 2.

    Returns
    -------
    grid : ndarray
        Array of DVR grid points
    D : ndarray
        First derivative operator in DVR basis, shape (num, num)
    D2 : ndarray (num,num)
        Symmetrized derivative operator (d^dagger * d) in DVR basis, shape (num, num)

    Notes
    -----
    The Legendre DVR is defined over a finite integration domain with
    non-periodic boundary conditions. The derivative operator is thus not
    stricty anti-Hermitian because of non-zero boundary terms. The `D2`
    matrix returned is the representation of the explicitly symmetrized
    :math:`-\partial^\dagger \cdot \partial`
    operator, which is *not* equivalent to :math:`\partial \cdot \partial`

    """

    # Construct coordinate operator in FBR
    XFBR = np.zeros((num,num))
    for p in range(num-1):

        val = np.sqrt(2*p+1) * np.sqrt(2*p+3) * (stop-start)/4.0 \
            * 2*(p+1) / ( (2*p+1)*(2*p+3))
        XFBR[p,p+1] = val
        XFBR[p+1,p] = val

    XFBR += np.eye(num)*(stop+start)/2.0

    # Calculate derivative operator matrix elements
    DFBR = np.zeros((num,num))
    D2FBR = np.zeros((num,num)) # -d^dagger * d
    for n in range(num):
        for m in range(num):

            if (m-n) % 2 == 1 and n <= m:
                DFBR[n,m] = np.sqrt( (2*n+1) * (2*m+1) ) * 2.0/(stop-start)

            if (m-n) % 2 == 0 :
                p = min(m,n)
                D2FBR[m,n] = -np.sqrt(2*m+1) * np.sqrt(2*n+1) \
                    * 2.0 / (stop-start)**2 * p*(p+1)

    # Diagonalize coordinate matrix in FBR
    w,v = np.linalg.eigh(XFBR)
    # Adjust phase of eigenfunctions
    for j in range(num):
        if v[0,j] < 0:
            v[:,j] *= -1.0
    # Calculate derivative operators in DVR
    D = v.transpose() @ DFBR @ v
    D2 = v.transpose() @ D2FBR @ v

    grid = w

    return grid, D, D2

def _legDVRwfs(q,start,stop,num):
    """
    Calculate Legendre DVR wavefunctions.

    Parameters
    ----------
    q : ndarray
        A 1D array of coordinate values.
    start : float
        DVR grid start value.
    stop : float
        DVR grid stop value.
    num : int
        The number of DVR grid points

    Returns
    -------
    wfs : ndarray
        A (`q`.size, `num`) shaped array with
        the DVR wavefunctions evaluated at grid points `q`.

    """

    if np.ndim(q) != 1:
        raise ValueError("q must be 1-dimensional")
    if stop <= start:
        raise ValueError("stop value must be larger than start value")
    if num < 2 :
        raise ValueError("num must be >= 2")

    nq = q.size

    # Construct coordinate operator in FBR
    XFBR = np.zeros((num,num))
    for p in range(num-1):

        val = np.sqrt(2*p+1) * np.sqrt(2*p+3) * (stop-start)/4.0 \
            * 2*(p+1) / ( (2*p+1)*(2*p+3))
        XFBR[p,p+1] = val
        XFBR[p+1,p] = val

    XFBR += np.eye(num)*(stop+start)/2.0

    # Diagonalize coordinate matrix in FBR
    w,v = np.linalg.eigh(XFBR)
    # Adjust phase of eigenfunctions
    for j in range(num):
        if v[0,j] < 0:
            v[:,j] *= -1.0

    z = (q - (start+stop)/2.0) * 2.0/(stop-start)

    # Calculate the standard Legendre polynomials
    # via the recurrence relation
    #  n*P_n = (2n-1) z P_{n-1}  - (n-1) P_{n-2}
    #
    leg = np.ndarray((nq,num), dtype = q.dtype)
    leg[:,0] = 1
    leg[:,1] = z
    for n in range(2,num):
        leg[:,n] = (2*n-1)/n * z*leg[:,n-1]  -  (n-1)/n * leg[:,n-2]
    # Now, normalize the functions for (start, stop)
    for n in range(0,num):
        leg[:,n] *= np.sqrt((2*n+1)/(stop-start))

    wfs = leg @ v

    return wfs


import numpy as np

class Stochastic_gradient_descent():

    def __init__(self,learning_rate=0.1):
        self.learning_rate=learning_rate

    def fit(self,X,Y):
        '''
        first finding the size of given features.

        m=no. of given training sets
        n=no.features in each training set

        after that we will assign the theta to
        the random values.

        '''
        m,n=X.shape
        self.m,self.n=X.shape

        # now initializing the theta.

        self.theta=np.random.random((self.n+1,1))
        self.X=X
        self.Y=Y
        '''
        we are adding a column vector having value 1,because
        the hypothesis function for stochastic gradient descent
        algo is:h(theta)=theta(0)+theta(1)*X(1)+theta(2)*X(2)...........
        so, In order to vectorize the hypothesis fn we will add column vector.
        Here is breif explanation: X.shape=(m,n) and theta.shape=(n+1,1)
        so, in order to multiply the both matrix, we have to add one column to
        X, so that X will become: (m,n+1)

        '''
        ones=np.ones(self.m,int).reshape(-1,1)
        self.X=np.concatenate((ones,self.X),axis=1)
        self.gradient_descent()
        return self

    def gradient_descent(self):

        #updating theta.
        for i in range(self.m):
            grad_cost=self.X[i,:]@self.theta-self.Y[i,:]
            self.theta=self.theta-(self.learning_rate*grad_cost*(self.X[i,:].reshape(-1,1)))
        return self

    def predict(self,X):
        m,n=X.shape
        ones=np.ones(m,int).reshape(-1,1)
        X=np.concatenate((ones,X),axis=1)
        return (X@self.theta)


"""
This is our script to post process predictions generated by our classifier on images, as well as any other data that needs post-processing.

Further documentation found in each function.

-Blake Edwards / Dark Element
"""
import sys
import numpy as np
from scipy.sparse import csr_matrix, eye
from scipy.sparse.linalg import bicg

def normalize_adjacency_matrix(adjacency_matrix):
    """
    Helper function for denoise_predictions.

    Arguments:
        adjacency_matrix: A matrix of size h * w,
            https://en.wikipedia.org/wiki/Adjacency_matrix

            For this type of normalization, should be symmetric - i.e. from an undirected graph.

    Returns:
        normalized_adjacency_matrix: A normalized version of our adjacency matrix,
            such that each entry represents:
                1) If the source and dest node are connected, and if so, then
                2) how connected the source node is
                3) how connected the destination node is
        If the input is symmetric, this output will still be symmetric.
        Though it could also be normalized in other ways.
    """
    """
    We sum over all the rows of this matrix to get the degrees,
        since it is a symmetric adjacency matrix this is the same as if we summed over the columns.

    Once we have the degrees vector, we use it as the data to create a new sparse matrix,
        of the same shape as our adjacency matrix, and with the vector elements on the diagonal
        via using (row, col) pairs of (0,0), (1,1), ..., (h*w-1, h*w-1) with (np.arange(h*w), np.arange(h*w))
    """
    """
    We get our h*w via the length of the matrix, since we don't need individual h and w values.
    """
    adjacency_matrix_len = adjacency_matrix.shape[0]
    degrees = adjacency_matrix.sum(axis=1)
    degrees = np.array(degrees).flatten()
    degree_matrix = csr_matrix((degrees, (np.arange(adjacency_matrix_len), np.arange(adjacency_matrix_len))), shape=(adjacency_matrix_len, adjacency_matrix_len))

    """
    We use the adjacency matrix and degree matrix to make our normalized adjacency matrix, via

        M = D^(-1/2) * A * D^(-1/2)

    Where
        D = degree matrix,
        A = adjacency matrix,
        M = normalized adjacency result

    Since our degree matrix is a diagonal matrix,
        the elementwise matrix power of it is the same
        as the normal matrix power, so we can quickly
        use the built-in csr_matrix function for elementwise power
        to compute D^(-1/2).

    We then do the dot product in the normal way.
    """
    degree_matrix = degree_matrix.power(-1./2)
    normalized_adjacency_matrix = degree_matrix.dot(adjacency_matrix.dot(degree_matrix))#DAD
    return normalized_adjacency_matrix

def denoise_predictions(predictions, neighbor_weight):
    """
    Arguments:
        predictions: np array of shape (h, w, class_n), the predictions.
            This will be referred to as an image of size h x w, as this makes
            notation much easier.
        neighbor_weight:
            How much importance to put on the neighbor values.
            This could also be thought of as a smoothing factor.
            Should be between 0 and 1

    Returns:
        Each "prediction" is an element in our predictions input,
            and is a vector of length class_n. There are h * w of these predictions
            in our entire predictions array.

        For each prediction vector in our predictions,
            we produce a new denoised prediction vector,
            taking into account both the nearby prediction vectors to this element,
            and the original prediction vector.

        Returns a new predictions array, of the same shape (h, w, class_n) as the original.
    """
    """
    First we need to create an adjacency matrix for each entry in our predictions array,
        as if each node was a prediction vector, so that the graph is a rectangle of shape h x w,
        and each node is connected to all of it's nearby nodes.

    Ex, with h = 3, w = 4:

    . . . .
    . . . . (I think it may be easier to just imagine the lines, due to limitations of text art)
    . . . .

    Such that the minimum amount of connections a node will have (unless there is a really really small h and w)
        will be 3, and the maximum will be 8.

    Since adjacency matrices are sparse (mostly zeros), we can save memory by using different compression algorithms to
        only store the nonzero elements. I'll be using scipy's CSR element, as this algorithm offers easy matrix-vector
        dot products, and as a result of this can be extended to matrix-matrix dot products - CSR is efficient when
        you need to perform linear algebra ops on your sparse matrices.

    Since the adjacency matrix will be of size (h*w, h*w), it quickly becomes absolutely necessary to use a compression format,
        or else numpy won't even be able to hold it in memory. For an example of h = 300, w = 400, the raw adjacency matrix
        would contain h*w * h*w elements, = 300*400 * 300*400 = 120000 * 120000 = 1.44e10 .

    Due to the simple nature of our graph, undirected and where each node is connected in a simple and deterministic manner to its neighbors,
        we can construct it with O(n) complexity. I designed a way to incrementally create each row in this matrix given the h and w,
        as this is all that's necessary given our knowledge of how the graph is constructed. I could loop through every element and construct it
        the way it would be done by hand, however this would have O(n^2) complexity, and since n = h*w, that is really really bad. For the sake
        of optimizing down to O(n), I had to observe and replicate the patterns in adjacency matrices of this type. It is not as obviously intuitive
        as the by-hand method, however it will run at O(n), able to loop through and construct rows of the adjacency matrix at a time.
    """
    """
    Prepare these lists for constructing our CSR matrix
    """
    rows = []
    cols = []
    data = []

    """
    Get this data from our shape
    """
    h, w, class_n = predictions.shape

    def ins(i, j, x):
        """
        Arguments:
            i: row index of x
            j: col index of x
            x: data to add
        Returns:
            Adds i, our row index, to the row indices list
            Adds j, our col index, to the col indices list
            Adds x, our data, to the data list

            This is just a quick function to save time in the next steps.
        """
        if j >= 0 and j < h*w:
            rows.append(i)
            cols.append(j)
            data.append(x)

    """
    Loop through and construct our rows
    """
    for i in range(h*w):
        if i % w == w-1:
            ins(i, i-(w+1), 1)
            ins(i, i-(w), 1)
            ins(i, i-1, 1)
            ins(i, i+(w-1), 1)
            ins(i, i+(w), 1)

        elif i % w == 0:
            ins(i, i-(w), 1)
            ins(i, i-(w-1), 1)
            ins(i, i+1, 1)
            ins(i, i+(w), 1)
            ins(i, i+(w+1), 1)

        else:
            ins(i, i-(w+1), 1)
            ins(i, i-(w), 1)
            ins(i, i-(w-1), 1)
            ins(i, i-1, 1)
            ins(i, i+1, 1)
            ins(i, i+(w-1), 1)
            ins(i, i+(w), 1)
            ins(i, i+(w+1), 1)

    """
    Now that lists are finished changing in size,
        we cast to nparrays
    """
    rows = np.array(rows)
    cols = np.array(cols)
    data = np.array(data)

    """
    Using our data and row and column indices, we create our adjacency matrix as a CSR Sparse matrix
    """
    adjacency_matrix = csr_matrix((data, (rows, cols)), shape=(h*w, h*w))

    """
    We then normalize this adjacency matrix
    """
    normalized_adjacency_matrix = normalize_adjacency_matrix(adjacency_matrix)

    """
    Then we use the following equation, from this paper:

        http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0051947

        (I - l*M)*f = (1-l)*y

    In order to solve for new values of y (our predictions), so that we have
        new predictions in f.

    That paper has the explanation for that equation.
    Once we have that equation, we can simplify so that:

        A = (I - l * M)
        x = f
        b = (1-l)*y

    Our equation is now:

        Ax = b ,

    Which is just the setup for a least squares approximation of x:

        https://en.wikipedia.org/wiki/Least_squares
        https://www.youtube.com/watch?v=MC7l96tW8V8 (Khan Academy)

    So we prepare those components first.
    """
    A = eye(h*w) - neighbor_weight * normalized_adjacency_matrix

    predictions = np.reshape(predictions, (-1, class_n))
    b = (1.-neighbor_weight) * predictions

    """
    Unfortunately, least squares usually expects x and b to be vectors.
    Right now, x is going to be of shape (h*w, class_n), and b is also of shape (h*w, class_n).
    These are very much not vectors, but matrices instead. So how can we extend least squares to work in this setting?

    It's actually very simple, due to one property.

    For the system Ax = b, where
        A is a matrix of size n x n
        x is a matrix of size n x m
        b is a matrix of size n x m,

    Let us look at a column vector in matrix b, at position i.
    Each element in this vector is determined entirely by a column vector in matrix x,
        at position i,
        as well as the entire matrix A.
    For an understanding of this, think about how the elements of this column vector in b are
        obtained through the dot product of A and x.

    What this means is that we can solve for the entire matrix x by solving for each of the columns
        in x independently, looping across both the columns we are solving for in x and the other matching column
        in b, and solving using normal least squares.

    Remember that x is our new prediction values, so this is the last math-heavy step.
    """
    x = np.zeros(b.shape, dtype=np.float32)
    for i in range(class_n):
        col = b[:,i]
        col = np.reshape(col, (-1, 1))
        """
        Solve for this column using Bi-Conjugate Gradient Iteration from scipy,
            and set the result column into our resulting x matrix.
        """
        x[:,i], flags = bicg(A, col)

    """
    At this point our entire x result matrix has been constructed,
        however it is of shape (h*w, class_n).
    So we reshape it into (h, w, class_n), and rename it to the more
        appropriate denoised_predictions - since these are our denoised predictions.
    """
    denoised_predictions = np.reshape(x, (h, w, class_n))
    return denoised_predictions


""" Distance Correlation

Notes
-----
Snippet adapted from: https://gist.github.com/Satra/aa3d19a12b74e9ab7941

"""
# Author: Avraam Marimpis <avraam.marimpis@gmail.com>

import numpy as np
from scipy.spatial.distance import pdist, squareform  # type: ignore

def dcorr(x: np.ndarray, y: np.ndarray) -> float:
    """ Distance Correlation

    Parameters
    ----------
    x : array-like, shape(n_samples)
        Input time series.

    y : array-like, shape(N)
        Input time series.

    Returns
    -------
    val : float
        The computed distance correlation.
    """
    lx = len(x)
    ly = len(y)
    if lx != ly:
        raise Exception("")

    X = np.atleast_1d(x)  # type: ignore
    Y = np.atleast_1d(y)  # type: ignore

    if np.prod(X.shape) == len(X):
        X = X[:, None]
    if np.prod(Y.shape) == len(Y):
        Y = Y[:, None]
    X = np.atleast_2d(X)
    Y = np.atleast_2d(Y)
    n = X.shape[0]

    a = squareform(pdist(X))
    b = squareform(pdist(Y))

    A = a - a.mean(axis=0)[None, :] - a.mean(axis=1)[:, None] + a.mean()
    B = b - b.mean(axis=0)[None, :] - b.mean(axis=1)[:, None] + b.mean()

    dcov2_xy = (A * B).sum() / float(n * n)
    dcov2_xx = (A * A).sum() / float(n * n)
    dcov2_yy = (B * B).sum() / float(n * n)
    dcor = np.sqrt(dcov2_xy) / np.sqrt(np.sqrt(dcov2_xx) * np.sqrt(dcov2_yy))

    return dcor


"""Module contains functions for compute similarity."""

from numba import njit
import numpy as np

@njit
def imk(dist, alpha):
    """
    Inverse Multiquadratic Kernel (IMK).

    Parameters
    ----------
    dist : float or numpy.ndarray, shape=(m,)
        Distances.

    alpha : float
        Control the decay of the function.

    Results
    -------
    sim : float or numpy.ndarray, shape=(m)
        Similatity result.

    """
    return 1 / np.sqrt(1 + alpha ** 2 * dist ** 2)

@njit
def wsf(dist, alpha):
    """
    Wegerich Similarity Function (WSF).

    Parameters
    ----------
    dist : float or numpy.ndarray, shape=(m,)
        Distances.

    alpha : float
        Control the decay of the function.

    Returns
    -------
    sim : float or numpy.ndarray, shape=(m)
        Similatity result.

    """
    return 1 / (1 + alpha * dist)

@njit
def cck(dist, alpha):
    """
    Cauchy Kernel Function (CCK).

    Parameters
    ----------
    dist : float or numpy.ndarray, shape=(m,)
        Distances.

    alpha : float
        Control the decay of the function.

    Returns
    -------
    sim : float or numpy.ndarray, shape=(m)
        Similatity result.

    """
    return 1 / (1 + (alpha ** 2) * (dist ** 2))

@njit
def lk(dist, alpha):
    """
    Exponential or Laplace Kernel (LK).

    Parameters
    ----------
    dist : float or numpy.ndarray, shape=(m,)
        Distances.

    alpha : float
        Control the decay of the function.

    Results
    -------
    sim : float or numpy.ndarray, shape=(m)
        Similatity result.

    """
    return np.exp(-alpha * dist)

@njit
def rbf(dist, alpha):
    """
    Radial Basis Function (RBF).

    Parameters
    ----------
    dist : float or numpy.ndarray, shape=(m,)
        Distances.

    alpha : float
        Control the decay of the function.

    Results
    -------
    sim : float or numpy.ndarray, shape=(m)
        Similatity result.

    """
    return np.exp(-alpha * dist ** 2)

def sto(dist, d, epsilon=1e-10):
    """
    Saturated Triangular Operator (STO).

    Eq.:
        d - dist, if dist <= d + epsilon
        epsilon, if dist > d + epsilon

    Parameters
    ----------
    dist : float or numpy.ndarray, shape=(m,)
        Distances.

    d : float
        Threshold based on observartions varince.

    epsilon : float
        Value for similarity when the distance is greater than `d`.

    Results
    -------
    sim : float or numpy.ndarray, shape=(m)
        Similatity result.

    """
    less_eq_d = np.less_equal(dist, d + epsilon)

    sim = dist.copy()
    sim[less_eq_d] = 1 - dist[less_eq_d] / d  # Adjust to interval [0, 1]
    sim[~less_eq_d] = epsilon

    return sim


# Basic OLS regression using base python (and numpy)

import numpy as np
import matplotlib.pyplot as plt

# Load data
dataset = np.genfromtxt('../data/regression_heart.csv', delimiter=",")

x = dataset[:,1:]
y = dataset[:,0]
y = np.reshape(y, (y.shape[0],1))  # Reshape to a column vector

# Scale (standardize) data for smoother gradient descent
x = (x - np.mean(x, axis=0)) / np.std(x, axis=0)
x = np.insert(x, 0, 1, axis=1)  # Add 1's for bias

# Learning parameters
alpha = 0.01  # Learning rate
iterations = 1000

# Notes
# Cost function for linear regression: 1/2m(sum((theta0 + theta1(x) - y) ^ 2))
# Partial derivative wrt theta0: 1/m(sum(theta0 + theta1 - y))
# Partial derivative wrt theta1: 1/m(sum(theta0 + theta1 - y)) * x
# Parameter update: theta = theta - alpha(partial derivative)

# Training
theta = np.ones((x.shape[1],1))  # Initial weights
m = y.shape[0]  # Number of training examples. Equivalent to X.shape[0]
cost_history = np.zeros(iterations)  # Initialize array of cost history values

for i in xrange(iterations):  # Batch gradient descent
    residuals = np.dot(x, theta) - y
    squared_error = np.dot(residuals.T, residuals)
    cost = 1.0/(2*m) * squared_error  # Quadratic loss

    gradient = 1.0/m * np.dot(residuals.T, x).T  # Calculate derivative
    theta -= (alpha * gradient)  # Update weights

    cost_history[i] = cost  # Store the cost for this iteration

    if (i+1) % 100 == 0:
        print "Iteration: %d | Cost: %f" % (i+1, cost)

# Plot training curve
plt.plot(range(1, len(cost_history)+1), cost_history)
plt.grid(True)
plt.xlim(1, len(cost_history))
plt.ylim(0, max(cost_history))
plt.title("Training Curve")
plt.xlabel("Iteration #")
plt.ylabel("Cost")


import random
import sys
import numpy as np
import skfuzzy as fuzz
from skfuzzy import control as ctrl
import matplotlib.pyplot as plt

def fuzzing(vel, dis, flag = 0):
    # Fuzzificacao
    velocidade = ctrl.Antecedent(np.arange(0, 101, 1), 'velocidade')
    distancia = ctrl.Antecedent(np.arange(0, 101, 1), 'distancia')
    pressao = ctrl.Consequent(np.arange(0, 101, 1), 'pressao')

    velocidade.automf(5)
    distancia.automf(5)

    # pressao['low'] = fuzz.trimf(pressao.universe, [0, 0, 50])
    # pressao['medium'] = fuzz.trimf(pressao.universe, [0, 50, 100])
    # pressao['high'] = fuzz.trimf(pressao.universe, [50, 100, 100])

    pressao['poor'] = fuzz.gaussmf(pressao.universe, 0, 10)
    pressao['mediocre'] = fuzz.gaussmf(pressao.universe, 25, 10)
    pressao['average'] = fuzz.gaussmf(pressao.universe, 50, 10)
    pressao['decent'] = fuzz.gaussmf(pressao.universe, 75, 10)
    pressao['good'] = fuzz.gaussmf(pressao.universe, 100, 10)

    # Regras
    regras = []

    regras.append(ctrl.Rule(velocidade['mediocre'] & distancia['average'], pressao['poor']))
    regras.append(ctrl.Rule(velocidade['mediocre'] & distancia['mediocre'], pressao['mediocre']))
    regras.append(ctrl.Rule(velocidade['mediocre'] & distancia['poor'], pressao['average']))
    regras.append(ctrl.Rule(velocidade['average']  & distancia['average'], pressao['average']))
    regras.append(ctrl.Rule(velocidade['average']  & distancia['mediocre'], pressao['decent']))
    regras.append(ctrl.Rule(velocidade['average']  & distancia['poor'], pressao['good']))
    regras.append(ctrl.Rule(velocidade['decent'] & distancia['good'], pressao['average']))
    regras.append(ctrl.Rule(velocidade['decent'] & distancia['decent'], pressao['decent']))

    regras.append(ctrl.Rule(velocidade['poor'], pressao['poor']))
    # regras.append(ctrl.Rule(velocidade['mediocre'] | distancia['good'], pressao['decent']))
    regras.append(ctrl.Rule(velocidade['good'], pressao['good']))

    pressao_ctrl = ctrl.ControlSystem(regras)
    pressao_sim = ctrl.ControlSystemSimulation(pressao_ctrl)

    pressao_sim.input['velocidade'] = vel
    pressao_sim.input['distancia'] = dis

    pressao_sim.compute()
    print(pressao_sim.output['pressao'])

    # distancia.view()
    # velocidade.view()
    pressao.view(sim=pressao_sim)
    plt.show()

if __name__ == '__main__':
    fuzzing(float(sys.argv[1]), float(sys.argv[2]))