import statsmodels.tsa.stattools as ts
from numpy import cumsum, log, polyfit, sqrt, std, subtract
from numpy.random import randn

from algo.result import *
from util.logger import *

# https://www.quantstart.com/articles/Basics-of-Statistical-Mean-Reversion-Testing/

# mean reversion is following the strategy based on statistical phenomenon that
# a time series set of values will return to its mean value

# we can determine mean and then do regression to the mean to come up with
# numerical deviations, a negative deviation converging towards the mean
# can be used to predict a rise in price to the actual historical mean

class MeanReversionDataSet_ADFuller():
    def __init__(self, data, data_str_name):
        self.data_name = data_str_name
        self.adfuller_data = ts.adfuller(data)

        #data
        self.calc_test_stat = self.adfuller_data[0] # the result, to test this should be MORE NEGATIVE than any of the critical values
        self.p_value = self.adfuller_data[1] # p-value (lower # = greater statistical relevance)
        self.num_values = self.adfuller_data[3] # total number of points read
        self.critical_values = self.adfuller_data[4] # critical values at 1,10,5%

    def __str__(self):
        return \
        "Mean Reversion Data\n" + \
        "Calculated Result: " + str(self.calc_test_stat) + \
        "P-Value: " + str(self.p_value) + \
        "Number of values given: " + str(self.num_values) + \
        "Critical Values " + str(self.critical_values)

    # will eventually return a result class
    def GetResult(self):
        print(self.__str__())
        res = self.Calculate()
        print(res.__str__())
        return res

    def Calculate(self):
        res = Result("Mean Reversion Result", False, "Mean Reversion failed on " + self.data_name)
        for crit_value_key in self.critical_values.keys():
            value = self.critical_values[crit_value_key]
            if value > self.calc_test_stat:
                res = Result("Mean Reversion Result",
                            True,
                            "Mean Reversion passed on " + self.data_name,
                            self.calc_test_stat,
                            "Calculated Result",
                            value,
                            crit_value_key)
        return res

# Used to calculate how close a set of data is to a random
# walk
# https://www.quantstart.com/articles/Basics-of-Statistical-Mean-Reversion-Testing/
class MeanReversionDataSet_Hurst():
    def __init__(self, data, data_str_name):
        self.data_name = data_str_name
        #data
        self.hurst_result = self.RunHurst(data)

    def RunHurst(self, ts):
        # Create the range of lag values
        lags = range(2, 100)

        # Calculate the array of the variances of the lagged differences
        tau = [sqrt(std(subtract(ts[lag:], ts[:-lag]))) for lag in lags]
        print(tau)
        print(lags)
        # Use a linear fit to estimate the Hurst Exponent
        poly = polyfit(log(lags), log(tau), 1)

        # Return the Hurst exponent from the polyfit output
        return poly[0]*2.0

    def GetResult(self):
        print(self.__str__())
        res = self.Calculate()
        print(res.__str__())
        return res

    def Calculate(self):
        # decide if: reverting, trending, about to walk
        # todo: verify if H is statistically significant
        # https://en.wikipedia.org/wiki/Confidence_interval
        walk = 0.5
        reverting = 0.0
        trending = 1.0

        walk_res = abs(self.hurst_result - walk)
        revert_res = abs(self.hurst_result - reverting)
        trending_res = abs(self.hurst_result - trending)

        if (walk_res < revert_res and walk_res < trending_res):
            return Result("Hurst Mean Reversion",
                        True,
                        self.data_name + " had a walk value: " + walk_res,
                        self.hurst_result,
                        "Calculated Hurst Result",
                        walk,
                        "Statistical Walk Value")
        if (revert_res < walk_res and revert_res < trending_res):
            return Result("Hurst Mean Reversion",
                        True,
                        self.data_name + " had a revert value: " + revert_res,
                        self.hurst_result,
                        "Calculated Hurst Result",
                        revert,
                        "Statistical Revert Value")

        if (trending_res < walk_res and trending_res < revert_res):
            return Result("Hurst Mean Reversion",
                        True,
                        self.data_name + " had a trending value: " + trending_res,
                        self.hurst_result,
                        "Calculated Hurst Result",
                        trending,
                        "Statistical Trending Value")

    def __str__(self):
        return \
        "Mean Reversion Data - Hurst\n" + \
        "Calculated Result: " + str(self.hurst_result)

class MeanReversion():
    def __init__(self):
        self.data = []

    def Calculate(self, data_name_str, data):
        calc_data_fuller = MeanReversionDataSet_ADFuller(data, data_name_str)
        calc_data_fuller.GetResult()
        self.data.append(calc_data_fuller)

        calc_data_hurst = MeanReversionDataSet_Hurst(data, data_name_str)
        calc_data_hurst.GetResult()
        self.data.append(calc_data_hurst)


import numpy as np

def distances(x, distance = "E", p = None):
    """
    Take a data matrix, return the distances matrix.

    Args:
        x: numpy array, data matrix.
        distance: {"E": Euclidean Distance, "Minkowski": Minkowski Distance, "Manhattan": Manhattan Distance,
                "C": Chebyshev Distance, "Mahalanobis": Mahalanobis Distance, "Corr": Correlation Coefficient,
                "cos": cosine}, default "E".
        p: If Minkowski distance is chosen, p should be set, default None.

    Return:
        Distances matrix.
    """
    if x.ndim == 1: x = np.reshape(x, [1, x.shape[0]])
    if x.ndim == 0: x = np.reshape(x, [1, 1])

    x = x.astype(np.float64)

    if distance == "Minkowski" and p == None:
        raise TypeError("Missing p for Minkowski distance.")

    if distance in  {"E", "Minkowski", "Manhattan", "C"}:
        if distance == "Manhattan": p = 1
        if distance == "E": p = 2
        res = np.zeros([1, x.shape[0]])
        for i in x:
            dis = np.sum(np.abs(x - i) ** p, axis = 1) ** (1/p) if distance != "C" else np.max(np.abs(x - i), axis = 1)
            dis = np.reshape(dis, [1, x.shape[0]])
            res = np.vstack([res, dis])
        return res[1:]

    if distance == "Mahalanobis":
        cov = np.cov(x.T)
        try:
            inv = np.linalg.inv(cov)
        except np.linalg.LinAlgError:
            raise ValueError("Cov matrix is singular matrix")
        res = np.zeros([1, x.shape[0]])
        for i in x:
            diff = x - i
            dis = np.diag(np.dot(np.dot(diff, inv), diff.T))
            res = np.vstack([res, dis])
        return res[1:]

    if distance == "Corr":
        return np.corrcoef(x)

    if distance == "cos":
        res = np.dot(x, x.T)
        for i in range(x.shape[0]):
            factor = res[i,i] ** 0.5
            res[:,i] = res[:,i] / factor
            res[i,:] = res[i,:] / factor
        return res


# -*- coding: utf-8 -*-
from numpy import log as npLog
from pandas_ta.utils import get_offset, verify_series

def entropy(close, length=None, base=None, offset=None, **kwargs):
    """Indicator: Entropy (ENTP)"""
    # Validate Arguments
    close = verify_series(close)
    length = int(length) if length and length > 0 else 10
    base = float(base) if base and base > 0 else 2.0
    offset = get_offset(offset)

    # Calculate Result
    p = close / close.rolling(length).sum()
    entropy = (-p * npLog(p) / npLog(base)).rolling(length).sum()

    # Offset
    if offset != 0:
        entropy = entropy.shift(offset)

    # Name & Category
    entropy.name = f"ENTP_{length}"
    entropy.category = "statistics"

    return entropy

entropy.__doc__ = \
"""Entropy (ENTP)

Introduced by Claude Shannon in 1948, entropy measures the unpredictability
of the data, or equivalently, of its average information. A die has higher
entropy (p=1/6) versus a coin (p=1/2).

Sources:
    https://en.wikipedia.org/wiki/Entropy_(information_theory)

Calculation:
    Default Inputs:
        length=10, base=2

    P = close / SUM(close, length)
    E = SUM(-P * npLog(P) / npLog(base), length)

Args:
    close (pd.Series): Series of 'close's
    length (int): It's period. Default: 10
    base (float): Logarithmic Base. Default: 2
    offset (int): How many periods to offset the result. Default: 0

Kwargs:
    fillna (value, optional): pd.DataFrame.fillna(value)
    fill_method (value, optional): Type of fill method

Returns:
    pd.Series: New feature generated.
"""


import numpy as np
import matplotlib.pyplot as plt
# from numpy import zeros

t0 = 0
tn = 100
h = 0.01
square_h = h**2/2
n = (tn - t0)/h

# first initial condition
x0 = -1.0
v0 = 0.1
f0 = -4 *x0*(x0**2 -1)

x = np.zeros(int(n))
v = np.zeros(int(n))
f = np.zeros(int(n))

x[0] = x0
v[0] = v0
f[0] = f0

# second initial condition
x01 = 1.0
v01 = 0.1
f01 = -4 *x01*(x01**2 -1)

x1 = np.zeros(int(n))
v1 = np.zeros(int(n))
f1 = np.zeros(int(n))

x1[0] = x01
v1[0] = v01
f1[0] = f01

# for loop for first initial conditions
for i in range(int(n-1)):
  # Velocity Verlet algorithm
  x[i+1] = x[i] + h * v[i] + square_h * f[i]
  f[i+1] = -4 * x[i] * (x[i]**2 -1)
  v[i+1] = v[i] + h * (f[i+1] + f[i])/2

# for loop for second initial conditions
for i in range(int(n-1)):
  x1[i+1] = x1[i] + h * v1[i] + square_h * f1[i]
  f1[i+1] = -4 * x1[i] * (x1[i]**2 -1)
  v1[i+1] = v1[i] + h * (f1[i+1] + f1[i])/2

# --------------plotting--------------
fig, ax = plt.subplots(1, 2)
fig.set_size_inches(10.5, 5.5)
fig.suptitle('Phase trajectory using Verlet Algorithm')

ax[0].plot(x, v, 'k')
ax[0].set(xlabel = 'x', ylabel='velocity (v)')
ax[0].set_title("(x0, p0)=" +"("+str(x0) + ',' + str(v0)+")")

ax[1].plot(x1, v1, 'b')
ax[1].set(xlabel = 'x')
ax[1].set_title(" (x0, p0)=" +"("+str(x01) + ',' + str(v01)+")")

# plt.savefig('Phase-Space-Velocity-Verlet.png')
plt.show()


import numpy as np
from numpy import linalg

def gcd(a, b):
    return gcd(b, a % b) if b != 0 else a

def extended_gcd(a, b):
    s, old_s = 1, 0
    t, old_t = 0, 1

    while b != 0:
        q = a // b
        a, b = b, a % b
        s, old_s = old_s, s - q * old_s
        t, old_t = old_t, t - q * old_t
    return a, s, t

def diophantine_sol_exists(a, b, c):
    hcf = gcd(a, b)
    return c % hcf == 0

# returns solution of diophantine equation in form (x, y) if exists
# returns the particular solution otherwise returns nothing
def diophantine(a, b, c):
    hcf = gcd(a, b)
    # solution exits
    if c % hcf == 0:
        _, s, t = extended_gcd(a / hcf, b / hcf)
        return int((c / hcf) * s), int((c / hcf) * t)

def diophantine_general(a, b, c, iters=10):
    if diophantine_sol_exists(a, b, c):
        x, y = diophantine(a, b, c)
        hcf = gcd(a, b)
        for i in range(iters):
            yield x + i * (b // hcf), y - i * (a // hcf)

def additive_inverse(a, n):
    return (n - a) % n

def multiplicative_inverse_exists(a, n):
    return gcd(a, n) == 1

def multiplicative_inverse(b, n):
    if multiplicative_inverse_exists(b, n):
        return (extended_gcd(n, b)[2] + n) % n

def sol_single_var_le(a, b, n, c=0):
    if c != 0:
        b = (b - c) % n

    hcf = gcd(a, n)
    sols = []
    if b % hcf == 0:
        a, b, n = a // hcf, b // hcf, n // hcf
        x_0 = (b * multiplicative_inverse(a, n)) % n
        for k in range(hcf):
            sols.append(x_0 + k * n)
    return sols

def modMatInv(A, p):  # Finds the inverse of matrix A mod p
    n = len(A)
    adj = np.zeros(shape=(n, n))
    for i in range(0, n):
        for j in range(0, n):
            adj[i][j] = ((-1) ** (i + j) * int(round(linalg.det(minor(A, j, i))))) % p
    return (modInv(int(round(linalg.det(A))), p) * adj) % p

def modInv(a, p):  # Finds the inverse of a mod p, if it exists
    for i in range(1, p):
        if (i * a) % p == 1:
            return i
    raise ValueError(str(a) + " has no inverse mod " + str(p))

def minor(A, i, j):  # Return matrix A with the ith row and jth column deleted
    A = np.array(A)
    sub_matrix = np.zeros(shape=(len(A) - 1, len(A) - 1))
    p = 0
    for s in range(0, len(sub_matrix)):
        if p == i:
            p = p + 1
        q = 0
        for t in range(0, len(sub_matrix)):
            if q == j:
                q = q + 1
            sub_matrix[s][t] = A[p][q]
            q = q + 1
        p = p + 1
    return sub_matrix

def is_prime(number: int) -> bool:
    for i in range(2, number):
        if number % i == 0:
            return False
    return True

def product(numbers: list) -> int:
    result = 1
    for number in numbers:
        result *= number
    return result

def relatively_prime(a: int, b: int) -> bool:
    return gcd(a, b) == 1

def exp_func(x, y):
    exp = bin(y)
    value = x

    for i in range(3, len(exp)):
        value = value * value
        if exp[i:i + 1] == '1':
            value = value * x
    return value

def mod_exponentiation(X, E, m):
    Y = 1
    while E > 0:
        if E % 2 == 0:
            X = (X * X) % m
            E /= 2
        else:
            Y = (X * Y) % m
            E -= 1
    return Y

def totient(number: int) -> int:
    count = 0
    for i in range(1, number):
        if gcd(number, i) == 1:
            count += 1
    return count

def relatively_prime_numbers(n):
    numbers = []
    for i in range(1, n):
        if relatively_prime(i, n):
            numbers.append(i)
    return numbers

def crt_representation(number: int, crt: tuple) -> list:
    result = []
    for mod in crt:
        result.append(number % mod)
    return result

def are_pairwise_relatively_prime(numbers) -> bool:
    for i, number in enumerate(numbers):
        for j in range(i + 1, len(numbers)):
            if not relatively_prime(number, numbers[j]):
                return False
    return True

# noinspection PyTypeChecker
def valid_crc(crc: tuple, number: int) -> bool:
    return are_pairwise_relatively_prime(crc) and product(crc) == number

def is_product_of_2_primes(number: int) -> bool:
    for i in range(2, number):
        if number % i == 0:
            quotient = number // i
            if is_prime(i) and is_prime(quotient):
                return True
    return False

def discrete_log_table(number: int) -> list:
    table = []
    for i in range(1, number):
        row = []
        for j in range(1, number):
            row.append(pow(i, j, number))
        table.append(row)
    return table

def discrete_log(n, b, m):
    for i in range(m):
        if pow(b, i, m) == n:
            return i


import numpy as np

def reg(u):
    u_reg = 1 / (np.linalg.norm(u)) * u
    u_reg_norm = np.linalg.norm(u_reg)

    print(f"{u_reg} , norm {u_reg_norm}")

reg(np.array([2, -7]))
reg(np.array([-9, 3, 7]))
reg(np.array([-3, 5, 8, 6]))
reg(np.array([-6, 2, 8, 3, 5]))

reg(np.array([5, 10, 4]))

vals = np.array([1,30,20,99,100,4])
n = np.linalg.norm(vals)

vals_n = vals * (1/n)
print(f"Norm {n}")
print(f"Norm vals {vals_n},  {np.linalg.norm(vals_n)}")


import numpy as np
import matplotlib.pyplot as plt

# function
def f(x):
  return 2*x[0]**2 + 10*x[1]**2

# gradient (normalized)
def fp(x):
  fx1 = 4*x[0]
  fx2 = 20*x[1]
  return np.array([fx1, fx2])

alpha = 0.01 # step size
x = np.array([3,3]) # initial point
xt = [] # empty lists to keep track of points we search
ft = []

# this is a "max-time" approach ...
# could also loop until the gradient is within a tolerance
normgrad = 9999
i = 0
maxiter = 1000

while normgrad > 10**-8 and i < maxiter:
  xt.append(x)
  ft.append(f(x))
  grad = fp(x)
  x = x - alpha*grad
  normgrad = np.sqrt(np.sum(grad**2))
  i += 1

print(i)

# just plotting stuff below here ...
plt.subplot(1,2,1)
xx = np.arange(-5,5,0.005)
X1,X2 = np.meshgrid(xx, xx)
Z = 2*X1**2 + 10*X2**2
plt.contour(X1,X2,Z,50,cmap=plt.cm.Blues_r)
xt = np.array(xt)
plt.plot(xt[:,0], xt[:,1], color='k', linewidth=2)
plt.xlabel('X1')
plt.ylabel('X2')

plt.subplot(1,2,2)
plt.plot(ft, color='k', linewidth=2)
plt.xlabel('Iterations')
plt.ylabel('Objective Value')
plt.show()


#!/usr/bin/env python

import numpy as np

#===================================================

def dejong(vector):
	# takes k-dimensional vector
	# continuous, convex, unimodal
	# global minimum: x_i = 0 for all i in range(k)
	vector = np.array(vector)
	# rescale onto [-5, 5]
	vector = 10 * vector - 5
	result = np.sum(vector**2)
	return result

#===================================================

def hyper_ellipsoid(vector):
	# takes k-dimensional vector
	# continuous, convex, unimodal
	# global minimum: x_i = 0 for all i in range(k)
	# rescale onto [-5, 5]
	vector = np.array(vector)
	vector = 10 * vector - 5
	weights = np.arange(1, len(vector) + 1)
	result = 0
	for index, element in enumerate(vector):
		result += weights[index] * element**2
	return result

#===================================================

def rosenbrock_function(vector):
	# takes k-dimensional vector
	# continuous, convex, unimodal, narrow valley
	# global minimum: x_i = 0 for all i in range(k)
	# rescale onto [-2, 2]
	vector = np.array(vector)
	vector = 4 * vector - 2
	result = 0
	for index, element in enumerate(vector[:-1]):
		result += 100 * (vector[index + 1] - element**2)**2  + (1 - element)**2
	return result

#===================================================

def rastrigin_function(vector):
	# takes k-dimensional vector
	# continuous, multi-modal, regular distribution of minima
	# global minimum: x_i = 0 for all i in range(k)
	result = 10. * len(vector)
	# rescale onto [-5, 5]
	vector = 10 * np.array(vector) - 5
	for index, element in enumerate(vector):
		result += element**2 - 10 * np.cos(2 * np.pi * element)
	return result

#===================================================

def schwefel_function(vector):
	# takes k-dimensional vector
	# continuous, multi-modal
	# global minimum far away from local minima
	# global minimum: 418.9829 * n at x_i = 420.9687
	# rescale onto [-500, 500]
	vector = 1000 * np.array(vector) - 500
	result = 0
	for index, element in enumerate(vector):
		result += - element * np.sin(np.sqrt(np.abs(element)))
	return result

#===================================================

def ackley_path_function(vector):
	# takes k-dimensional vector
	# continuous, multi-model
	# global minimum: x_i = 0
	# rescale onto [-32, 32]
	vector = 64 * np.array(vector) - 32
	a = 20.
	b = 0.2
	c = 2 * np.pi
	n = float(len(vector))
	vector = np.array(vector)
	result = - a * np.exp( - b * np.sqrt(np.sum(vector**2) / n ) ) - np.exp( np.sum(np.cos(c * vector)) / n ) + a + np.exp(1.)
	return result

#===================================================

def linear_funnel(vector):
	# takes k-dimensional vector
	vector = np.array(vector)
	vector = 10 * vector - 5
	bounds = [0.5, 1.5, 2.5, 3.5, 4.5]
	result = 5
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1
	result = np.amin([4, result])
	return result

def narrow_funnel(vector):
	# takes k-dimensional vector
	vector = np.array(vector)
	vector = 100 * vector - 50
	bounds = [1.0, 2.0, 3.0, 4.0, 5.0]
	bounds = np.array(bounds)**2
	result = 5
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1
	result = np.amin([4, result])
	return result

def discrete_ackley(vector):
	# takes k-dimensional vector
	vector = np.array(vector)
	vector = 100 * vector - 50
	bounds = [1.0, 2.0, 3.0, 4.0, 5.0]
	bounds = np.array(bounds)**2
	result = 5
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1
	bounds = [1.25, 2.0, 2.5]
	bounds = np.array(bounds)**2

	domain = np.linspace(-50, 50, 10)
	dx = domain[1] - domain[0]
	imaged = np.array([np.amin(np.abs(element - domain)) for element in vector])
	new_res = 5
	for bound in bounds[::-1]:
		if np.amax(np.abs(imaged)) < bound:
			new_res -= 1
		result = np.amin([result, new_res])
	result = np.amin([4, result])
	return result

def discrete_michalewicz(vector):
	vector = np.array(vector)
	vector = 100 * vector - 40

	bounds = [1.0, 2.0, 3.0, 4.0, 5.0]
	bounds = np.array(bounds)**2
	result = 5
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1
	bounds = [1.25, 2.0, 2.5, 3.0]
	bounds = np.array(bounds)**2
	new_res = 5
	for bound in bounds[::-1]:
		if np.amin(np.abs(vector)) < bound:
			new_res -= 1
	result = np.amin([result, new_res, 4])
	return result

def double_well(vector):
	vector = np.array(vector)
	vector = 100 * vector - 15

	bounds = [1.25, 1.75, 2.0, 2.5, 2.75]
	bounds = np.array(bounds)**2
	result = 5
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1

	vector -= 50
	vector[0] += 10
	bounds = [2.5, 4.0, 5.0, 6.5]
	bounds = np.array(bounds)**2
	new_res = 5
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			new_res -= 1

	result = np.amin([result, 4, new_res])
	return result

def discrete_valleys(vector):
	vector = np.array(vector)
	vector = 100 * vector - 50
	result = 5

	vector = vector - 50
	bounds = [1.25, 2.0, 2.5, 3.0]
	bounds = np.array(bounds)**2
	new_res = 5
	for bound in bounds[::-1]:
		if np.amin(np.abs(vector)) < bound:
			new_res -= 1
	result = np.amin([result, new_res, 4])

	vector = vector + 25
	bounds = [1.25, 2.0, 2.5, 3.0]
	bounds = np.array(bounds)**2
	new_res = 5
	for bound in bounds[::-1]:
		if np.amin(np.abs(vector)) < bound:
			new_res -= 1
	result = np.amin([result, new_res, 4])

	vector = vector + 25
	bounds = [1.25, 2.0, 2.5, 3.0]
	bounds = np.array(bounds)**2
	new_res = 5
	for bound in bounds[::-1]:
		if np.amin(np.abs(vector)) < bound:
			new_res -= 1
	result = np.amin([result, new_res, 4])

	vector = vector + 25
	bounds = [1.25, 2.0, 2.5, 3.0]
	bounds = np.array(bounds)**2
	new_res = 5
	for bound in bounds[::-1]:
		if np.amin(np.abs(vector)) < bound:
			new_res -= 1
	result = np.amin([result, new_res, 4])

	vector = vector + 25
	bounds = [1.25, 2.0, 2.5, 3.0]
	bounds = np.array(bounds)**2
	new_res = 5
	for bound in bounds[::-1]:
		if np.amin(np.abs(vector)) < bound:
			new_res -= 1
	result = np.amin([result, new_res, 4])

	vector = vector - 12.5
	bounds = [1.25, 1.5, 1.75, 2.0, 2.25]
	bounds = np.array(bounds)**2
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1

	vector[1] -= 50.
	bounds = [1.5, 1.75, 2.0]
	bounds = np.array(bounds)**2
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1

	vector[0] -= 50.
	bounds = [1.5, 1.75, 2.0]
	bounds = np.array(bounds)**2
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1

	vector[1] -= 25.
	bounds = [1.5, 1.75, 2.0]
	bounds = np.array(bounds)**2
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1

	vector[1] += 75.
	bounds = [1.5, 1.75, 2.0]
	bounds = np.array(bounds)**2
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1

	vector[1] -= 25.
	vector[0] += 25.
	bounds = [1.5, 1.75, 2.0]
	bounds = np.array(bounds)**2
	for bound in bounds[::-1]:
		if np.amax(np.abs(vector)) < bound:
			result -= 1

	if result < 0:
		result = 0
	return result

#======================================================

if __name__ == '__main__':

	import matplotlib.pyplot as plt
	import seaborn as sns
	import matplotlib.cm as cm
	sns.set_context('paper', font_scale = 2.0, rc = {'lines.linewidth': 3})
	sns.set_style('ticks')

	fig = plt.figure(figsize = (9, 6))
	ax0 = plt.subplot2grid((2, 3), (0, 0))
	ax1 = plt.subplot2grid((2, 3), (0, 1))
	ax2 = plt.subplot2grid((2, 3), (0, 2))
	ax3 = plt.subplot2grid((2, 3), (1, 0))
	ax4 = plt.subplot2grid((2, 3), (1, 1))
	ax5 = plt.subplot2grid((2, 3), (1, 2))
	axs = [ax0, ax1, ax2, ax3, ax4, ax5]

	plt.setp(ax1.get_yticklabels(), visible = False)
	plt.setp(ax2.get_yticklabels(), visible = False)
	plt.setp(ax4.get_yticklabels(), visible = False)
	plt.setp(ax5.get_yticklabels(), visible = False)
	plt.setp(ax0.get_xticklabels(), visible = False)
	plt.setp(ax1.get_xticklabels(), visible = False)
	plt.setp(ax2.get_xticklabels(), visible = False)

	losses = [linear_funnel, narrow_funnel, double_well, discrete_ackley, discrete_michalewicz, discrete_valleys]

	for loss_index, loss in enumerate(losses):
		domain = np.linspace(0, 1, 100)

		X, Y = np.meshgrid(domain, domain)
		Z = np.zeros((len(domain), len(domain)))
		for x_index, x in enumerate(domain):
			for y_index, y in enumerate(domain):
				value = loss([x, y])
				Z[x_index, y_index] = value

		levels = np.linspace(-0.5, 4.5, 6)

		pc = axs[loss_index].contourf(X, Y, Z, cmap = cm.RdYlBu, levels = levels)

	cbaxes = fig.add_axes([1.0, 0.115, 0.03, 0.815])
	cbar = plt.colorbar(pc, cax = cbaxes)
	cbar.set_ticks(range(5))
	cbar.set_ticklabels(range(5))

	plt.tight_layout()
	plt.savefig('discrete_loss_functions.png', bbox_inches = 'tight')
	plt.show()


"""
Name  : fd4.py
Author: Ajay Lotekar
e-mail: ablotekar@gmail.com
Date  : 2021-07-04

DESC  :
"""
import numpy as np

def fd4(z, xd):
    """ Forth order central finite difference

    Function compute first order derivative of given array by using 4th
    order central finite difference scheam

    :param z: Input array
    :param xd: Grid size
    :return: y: output vector
    """
    y = np.zeros(z.shape)
    for i in range(2, max(z.shape) - 3):
        y[i] = (8 * z[i + 1] - 8 * z[i - 1] - z[i + 2] + z[i - 2]) / (12 * xd)
    return y


import matplotlib.pyplot as plt
import numpy as np

SHAPE = (256, 256)
RES = (16, 16)

def generate_perlin_noise_2d(shape, res):
    def f(t):
        return 6 * t ** 5 - 15 * t ** 4 + 10 * t ** 3

    delta = (res[0] / shape[0], res[1] / shape[1])
    d = (shape[0] // res[0], shape[1] // res[1])
    grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]].transpose(1, 2, 0) % 1
    # Gradients
    angles = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1)
    gradients = np.dstack((np.cos(angles), np.sin(angles)))
    g00 = gradients[0:-1, 0:-1].repeat(d[0], 0).repeat(d[1], 1)
    g10 = gradients[1:, 0:-1].repeat(d[0], 0).repeat(d[1], 1)
    g01 = gradients[0:-1, 1:].repeat(d[0], 0).repeat(d[1], 1)
    g11 = gradients[1:, 1:].repeat(d[0], 0).repeat(d[1], 1)
    # Ramps
    n00 = np.sum(grid * g00, 2)
    n10 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1])) * g10, 2)
    n01 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1] - 1)) * g01, 2)
    n11 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1] - 1)) * g11, 2)
    # Interpolation
    t = f(grid)
    n0 = n00 * (1 - t[:, :, 0]) + t[:, :, 0] * n10
    n1 = n01 * (1 - t[:, :, 0]) + t[:, :, 0] * n11
    return np.sqrt(2) * ((1 - t[:, :, 1]) * n0 + t[:, :, 1] * n1)

def generate_fractal_noise_2d(shape, res, octaves=1, persistence=0.5):
    noise = np.zeros(shape)
    frequency = 1
    amplitude = 1
    for _ in range(octaves):
        noise += amplitude * generate_perlin_noise_2d(shape, (frequency*res[0], frequency*res[1]))
        frequency *= 2
        amplitude *= persistence
    return noise

# image = generate_perlin_noise_2d(SHAPE, (4, 4))
# plt.imshow(image, cmap='gray')
# plt.show()
#
# image[image > 0.5] = 1.0
# image[image <= 0.5] = 0.0
# plt.imshow(image, cmap='gray')
# plt.show()

fig, axs = plt.subplots(2, 2)

image = generate_perlin_noise_2d(SHAPE, (1, 1))
axs[0][0].imshow(image, cmap='gray')

image = generate_perlin_noise_2d(SHAPE, (2, 2))
axs[0][1].imshow(image, cmap='gray')

image = generate_perlin_noise_2d(SHAPE, (8, 8))
axs[1][0].imshow(image, cmap='gray')

image = generate_perlin_noise_2d(SHAPE, (16, 16))
axs[1][1].imshow(image, cmap='gray')

plt.show()

fig, axs = plt.subplots(2, 2)

image = generate_fractal_noise_2d((256, 256), (2, 2), octaves=3, persistence=0.25)
axs[0][0].imshow(image, cmap='gray')
print(np.max(image))

image = generate_fractal_noise_2d((256, 256), (4, 4), octaves=3, persistence=0.25)
axs[0][1].imshow(image, cmap='gray')
print(np.max(image))

image = generate_fractal_noise_2d((256, 256), (8, 8), octaves=3, persistence=0.25)
axs[1][0].imshow(image, cmap='gray')
print(np.max(image))

image = generate_fractal_noise_2d((256, 256), (16, 16), octaves=3, persistence=0.25)
axs[1][1].imshow(image, cmap='gray')
print(np.max(image))

plt.show()


'''
scalar_quantization_utils.py

'''

from scipy.integrate import quad
from scipy.stats import uniform, expon, gamma, rayleigh, norm
import numpy as np
from numpy import linalg as LA

def ReconLevel(func, a, b, mu, sigma):
    # Optimum quantizer
    # Calculates and returns a tupil (recon_level, recon_error)
    # func = sample pdf ('uniform', 'expon', 'gamma', 'rayleigh', 'norm')
    # a = quantization region left boundary
    # b = quantization region right boundary
    # mu = pdf mean
    # sigma = pdf standard deviation

    if func == 'uniform':
        y1, _ = quad(lambda x : x * uniform.pdf(x, mu, sigma), a, b);
        y2, _ = quad(uniform.pdf, a, b, args = (mu, sigma));
        recon_level = y1 / y2;

        mse, _ = quad(lambda x : (x - recon_level) ** 2 * uniform.pdf(x, mu, sigma), a, b);
    elif func == 'expon':
        y1, _ = quad(lambda x : x * expon.pdf(x, mu, sigma), a, b);
        y2, _ = quad(expon.pdf, a, b, args = (mu, sigma));
        recon_level = y1 / y2;

        mse, _ = quad(lambda x : (x - recon_level) ** 2 * expon.pdf(x, mu, sigma), a, b);
    elif func == 'gamma':
        y1, _ = quad(lambda x : x * gamma.pdf(x, mu, sigma), a, b);
        y2, _ = quad(gamma.pdf, a, b, args = (mu, sigma));
        recon_level = y1 / y2;

        mse, _ = quad(lambda x : (x - recon_level) ** 2 * gamma.pdf(x, mu, sigma), a, b);
    elif func == 'rayleigh':
        y1, _ = quad(lambda x : x * rayleigh.pdf(x, mu, sigma), a, b);
        y2, _ = quad(rayleigh.pdf, a, b, args = (mu, sigma));
        recon_level = y1 / y2;

        mse, _ = quad(lambda x : (x - recon_level) ** 2 * rayleigh.pdf(x, mu, sigma), a, b);
    elif func == 'norm':
        y1, _ = quad(lambda x : x * norm.pdf(x, mu, sigma), a, b);
        y2, _ = quad(norm.pdf, a, b, args = (mu, sigma));
        recon_level = y1 / y2;

    recon_error, _ = quad(lambda x : (x - recon_level) ** 2 * norm.pdf(x, mu, sigma), a, b);

    return recon_level, recon_error;

def MaxLloydQuantizer(func, limit_left, limit_right, N_levels, mu, sigma, tol = 10e-6):
    # Optimum iterative quantizer
    # Calculates and returns a tupil (quant_intervals, recon_level, recon_error)
    # func = sample pdf ('uniform', 'expon', 'gamma', 'rayleigh', 'norm')
    # limit_left = quantization left support region
    # limit_right = quantization right support region
    # N_levels = number of quantization levels
    # mu = mean
    # sigma = standard deviation
    # tol = tolerance

    step = (limit_right - limit_left) / N_levels;
    quant_intervals = [limit_left + step * i for i in range(N_levels)] + [limit_right];

    recon_levels = [ReconLevel(func, quant_intervals[i], quant_intervals[i + 1], mu, sigma)[0] for i in range(N_levels)];
    recon_error_old = limit_right - limit_left;
    recon_error = sum([ReconLevel(func, quant_intervals[i], quant_intervals[i + 1], mu, sigma)[1] for i in range(N_levels)]);

    while (abs(recon_error - recon_error_old) < tol * recon_error):
        recon_error_old = recon_error;
        quant_intervals = [limit_left] + [(recon_levels[i] + recon_levels[i + 1]) / 2 for i in range(N_levels - 1)] + [limit_right];
        recon_levels = [ReconLevel(func, quant_intervals[i], quant_intervals[i + 1], mu, sigma)[0] for i in range(N_levels)];
        recon_error = sum([ReconLevel(func, quant_intervals[i], quant_intervals[i + 1], mu, sigma)[1] for i in range(N_levels)]);

    return quant_intervals, recon_levels, recon_error

def ApplyQuantization(quant_intervals, recon_levels, signal_input):
    # Returns a tupil (signal_output, quant_error, SQNR)
    # quant_intervals = array of quantization intervals
    # recon_levels = array of quantization levels
    # signal_input = array of input signal

    signal_output = np.zeros(signal_input.shape);

    N_levels = len(recon_levels);
    for i in range(N_levels):
        if i != N_levels - 1:
            mask1 = signal_input >= quant_intervals[i];
            mask2 = signal_input < quant_intervals[i + 1];
            mask = mask1 * mask2;
        else:
            mask1 = signal_input >= quant_intervals[i];
            mask2 = signal_input <= quant_intervals[i + 1];
            mask = mask1 * mask2;

        signal_output[mask] = recon_levels[i];

    quant_error = signal_input - signal_output;
    SQNR = 20 * np.log10(LA.norm(signal_input) / LA.norm(quant_error));

    return signal_output, quant_error, SQNR;


import numpy as np
import time
import pandas as pd

def main():
    # initi conditions---------------------------------------------------------
    A=2
    B=0.01
    t0=0.0
    t_end=5
    #--------------------------------------------------------------------------

    # Analytic solution
    analytic = lambda t_ini, y_ini, t, a, b: 1/(b/a+(1/y_ini-b/a)*np.exp(-a*(t-t_ini)))
    #--------------------------------------------------------------------------
    # ODE
    f = lambda y: A*y-B*y*y
    #--------------------------------------------------------------------------
    # initial valuse
    y0_list = np.array([8, 10, 12])
    #y0_list = np.array([100, 190, 250])
    #--------------------------------------------------------------------------

    timesteps = np.array([0.05, 0.025, 0.0125, 0.00625, 0.003125])

    # maps of 8th order for different time steps (A=2, B=0.01)
    # M(0,:) is map for dt=0.05     (N=100)
    # M(1,:) is map for dt=0.025    (N=200)
    # M(2,:) is map for dt=0.0125   (N=400)
    # M(3,:) is map for dt=0.00625  (N=800)
    # M(4,:) is map for dt=0.003125 (N=1600)
    Maps = np.array([[-4.99601921667887e-24,1.84935964133497e-20,-4.23629183829682e-17,8.39174629410321e-14,-1.60590216195728e-10,3.05590456029527e-7,-0.000581157957616179,1.10517085683809,1.34511594564678e-6],
                    [-4.87731111635685e-26,2.78047377145932e-22,-1.15484975507109e-18,4.5376519355888e-15,-1.77104564948567e-11,6.90875050990476e-8,-0.000269499102660927,1.05127109608626,6.4017323733666e-9],
                    [-4.26178099855912e-28,4.13410513256993e-24,-3.32748495857332e-20,2.631715681418e-16,-2.07925096139956e-12,1.64269661317723e-8,-0.000129779879232919,1.02531512052318,2.76214379044979e-11],
                    [-3.52165980224463e-30,6.23344675198481e-26,-9.96204381136311e-22,1.58422251710012e-17,-2.51895391085821e-13,4.00518933883553e-9,-6.36833449188686e-5,1.01257845154063,2.10469969587637e-13],
                    [-2.82967746195301e-32,9.53610182697698e-28,-3.04610757006006e-23,9.71730546503066e-19,-3.09983113737682e-14,9.88849362981926e-10,-3.15443976843614e-5,1.00626957200376,-6.11683690894885e-14],
                    ])

    #--------------------------------------------------------------------------
    # resulting table
    # result(:, 0) is N
    # result(:, 1) is err_RK4
    # result(:, 2) is err_TM8
    # result(:, 3) is time_RK4
    # result(:, 4) is time_TM8
    # result(:, 5) is time_ratio = time_RK4/time_TM8
    result = np.zeros((len(timesteps), 6))

    for k, dt in enumerate(timesteps):  # for each time stemp dt
        M = Maps[k, :]                 # get TM8 for this dt
        N = int((t_end-t0)/dt)
        t = np.arange(t0, t_end, dt)

        result[k, 0] = N

        for y0 in y0_list: # for each y0
            # analytic solution------------------------------------------------
            y_sol = analytic(t0, y0, t, A, B)
            #------------------------------------------------------------------

            # RK4 integration--------------------------------------------------
            y_rk4 = np.zeros(N)

            y_rk4[0]=y0
            tic = time.time()
            for i in range(N-1):
                y = y_rk4[i]
                k1 = f(y)
                k2 = f(y+dt*k1/2)
                k3 = f(y+dt*k2/2)
                k4 = f(y+dt*k3)
                y_rk4[i+1] = y + dt*(k1+2*k2+2*k3+k4)/6

            elapsed_time = time.time()-tic
            result[k, 3] += elapsed_time # time_RK4
            #------------------------------------------------------------------

            # Mapping----------------------------------------------------------
            y_map = np.zeros(N)
            y_map[0] = y0
            tic = time.time()
            for i in range(N-1):
                y=y_map[i]
                #y_map[i+1] = M[0]*y**8+M[1]*y**7+M[2]*y**6+M[3]*y**5+M[4]*y**4+M[5]*y**3+M[6]*y**2+M[7]*y+M[8]
                #y_map[i+1] = +M[8]
                y2 = y*y
                y3 = y2*y
                y4 = y3*y
                y_map[i+1] = (y4*(M[0]*y4 + M[4]) +
                            y3*(M[1]*y4 + M[5]) +
                            y2*(M[2]*y4 + M[6]) +
                            y *(M[3]*y4 + M[7]) +
                            M[8]
                            )

            elapsed_time = time.time()-tic
            result[k, 4] += elapsed_time # time_RK4
            #------------------------------------------------------------------
            result[k, 1] += np.abs(y_rk4 - y_sol).max() # err_RK4
            result[k, 2] += np.abs(y_map - y_sol).max() # err_TM8

    result[:, 1:] /= len(y0_list) # get average results
    result[:, 5] = result[:, 3]/result[:, 4] # get time_ratio

    result = pd.DataFrame(data=result[:,1:], index=result[:,0], columns=np.array(['err_RK4', 'err_TM8', 'time_RK4', 'time_TM8', 'time_ratio']))
    return result

if __name__ == "__main__":
    print(main())


import numpy as np
import scipy.stats.mstats
import matplotlib.pyplot as plt
from PIL import Image
import os

def create_dir(dir_name):
    if not os.path.exists(dir_name):
        os.makedirs(dir_name)

def corr2_coeff(A, B):
    # Rowwise mean of input arrays & subtract from input arrays themeselves
    A_mA = A - A.mean()
    B_mB = B - B.mean()

    # Sum of squares across rows
    ssA = (A_mA ** 2).mean()
    ssB = (B_mB ** 2).mean()

    # Finally get corr coeff
    coef = (A_mA * B_mB).mean() / np.sqrt(ssA * ssB)
    return coef

def r2coef(gt, pred):
    gt_mean = gt.mean()
    r2coef = 1 - np.sum((gt - pred) ** 2) / np.sum((gt - gt_mean) ** 2)
    return r2coef

def get_rmse(gt, pred):
    return np.sqrt(np.mean((gt - pred) ** 2))

def get_kl(gt, pred, chance=1e-5): # Kullback-Leibler divergence
    kl = np.sum(gt * np.where(gt > chance, np.log(gt), 0) - gt * np.where(pred >= chance, np.log(pred), 0))
    return kl

def get_spearmanr(gt, pred):
    try:
        return scipy.stats.spearmanr(gt, pred)[0]
    except:
        return 0

def label_accuracy(label_trues, label_preds):
    """Returns accuracy score evaluation result."""

    gt = label_trues.astype(np.float64)
    pred = label_preds.astype(np.float64)
    gt_1d, pred_1d = gt.flatten() / gt.sum(), pred.flatten() / pred.sum()
    gt_1d_01, pred_1d_01 = gt.flatten() / 255.0, pred.flatten() / 255.0

    cc = corr2_coeff(gt_1d, pred_1d)
    kl = get_kl(gt_1d, pred_1d)
    kl_01 = get_kl(gt_1d_01, pred_1d_01)
    spearman = get_spearmanr(gt_1d, pred_1d)
    r2 = r2coef(gt_1d, pred_1d)
    rmse = get_rmse(gt_1d_01, pred_1d_01)

    return kl, kl_01, cc, rmse, r2, spearman

def overlay_imp_on_img(img, imp, fname, colormap='jet'):

    cm = plt.get_cmap(colormap) # https://matplotlib.org/examples/color/colormaps_reference.html
    img2 = np.array(img, dtype=np.uint8)
    imp2 = np.array(imp, dtype=np.uint8)
    imp3 = (cm(imp2)[:, :, :3] * 255).astype(np.uint8)
    img3 = Image.fromarray(img2)
    imp3 = Image.fromarray(imp3)
    im_alpha = Image.blend(img3, imp3, 0.5)
    im_alpha.save(fname)


import numpy as np
from algorith.arrays.binary_srch import binary_search

def find_k_ix(a1,i1,j1,a2,i2,j2,k):
    n1=j1-i1+1
    n2=j2-i2+1
    if k==0:
        return min(a1[i1],a2[i2])
    elif k==n1+n2-1:
        return max(a1[j1],a2[j2])
    elif k>n1+n2-1:
        raise Exception("Out of bounds")
    if n1<n2:
        n_s=n1; n_l=n2; i_s=i1; i_l=i2
        a_s=a1; a_l=a2; j_s=j1; j_l=j2
    else:
        n_s=n2; n_l=n1; i_s=i2; i_l=i1
        a_s=a2; a_l=a1; j_s=j2; j_l=j1
    if n_s==1:
        if a_l[k]<a_s[i_s] and a_s[i_s]<a_l[k+1]:
            return a_s[i_s]
        elif a_s[i_s]<a_l[k]:
            return a_l[k+1]
        else:
            return a_l[k]
    if k>=n_s:
        cand = a_l[i_l+k-n_s]
        ix = binary_search(a_s,cand,i_s,j_s)
        if ix==len(a_s)-1:
            return cand
        else:
            return find_k_ix(a_s,ix+1,j_s,a_l,i_l+k-n_s+1,j_l,n_s+i_s-ix-2)
    else:
        if a_s[i_s]<a_l[i_l]:
            return find_k_ix(a_s,i_s+1,i_s+k,a_l,i_l,i_l+k,k-1)
        else:
            return find_k_ix(a_s,i_s,i_s+k,a_l,i_l+1,i_l+k,k-1)

def find_median(a1,a2):
    n = len(a1)+len(a2)
    if n%2==1:
        return find_k_ix(a1,0,len(a1)-1,a2,0,len(a2)-1,n//2)
    else:
        num1=find_k_ix(a1,0,len(a1)-1,a2,0,len(a2)-1,n//2)
        num2=find_k_ix(a1,0,len(a1)-1,a2,0,len(a2)-1,n//2+1)
        return (num1+num2)/2

if __name__=="__main__":
    a11=np.array([ 3,  6,  8, 11, 13, 14])
    a21=np.array([ 0,  1,  2,  4,  5,  7,  9, 10, 12])
    mm1 = find_k_ix(a11,0,len(a11)-1,a21,0,len(a21)-1,5)
    print(mm1)


# Simulate a Cox point process on a disk.
# This Cox point process is formed by first simulating a uniform Poisson
# line process. On each line (or segment) an independent Poisson point
# process is then simulated.
# Author: H. Paul Keeler, 2019.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts
# For more details, see the post:
# hpaulkeeler.com/simulating-a-cox-point-process-based-on-a-poisson-line-process/

import numpy as np;  # NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt  # for plotting
from matplotlib import collections  as mc  # for plotting line segments

plt.close('all');  # close all figures

###START -- Parameters -- START###
# Cox (ie Poisson line-point) process parameters
lambda0 = 4;  # intensity (ie mean density) of the Poisson *line* process
mu = 5;  # intensity (ie mean density) of the Poisson *point* process

# Simulation disk dimensions
xx0 = 0;
yy0 = 0;  # center of disk
r = 1;  # disk radius
massLine = 2 * np.pi * r * lambda0;
###END -- Parameters -- END###

###START -- Simulate a Poisson line process on a disk -- START###
# Simulate Poisson point process
numbLines = np.random.poisson(massLine);  # Poisson number of points

theta = 2 * np.pi * np.random.rand(numbLines);  # choose angular component uniformly
p = r * np.random.rand(numbLines);  # choose radial component uniformly
q = np.sqrt(r ** 2 - p ** 2);  # distance to circle edge (alonge line)

# calculate trig values
sin_theta = np.sin(theta);
cos_theta = np.cos(theta);

# calculate segment endpoints of Poisson line process
xx1 = xx0 + p * cos_theta + q * sin_theta;
yy1 = yy0 + p * sin_theta - q * cos_theta;
xx2 = xx0 + p * cos_theta - q * sin_theta;
yy2 = yy0 + p * sin_theta + q * cos_theta;
###END Simulate a Poisson line process on a disk END###

###START Simulate a Poisson point processes on each line START###
lengthLine = 2 * q;  # length of each segment
massPoint = mu * lengthLine;  # mass on each line
numbLinePoints = np.random.poisson(massPoint);  # Poisson number of points on each line
numbLinePointsTotal = sum(numbLinePoints);
uu = 2 * np.random.rand(numbLinePointsTotal) - 1;  # uniform variables on (-1,1)

# replicate values to simulate points all in one step
xx0_all = np.repeat(xx0, numbLinePointsTotal);
yy0_all = np.repeat(yy0, numbLinePointsTotal);
p_all = np.repeat(p, numbLinePoints);
q_all = np.repeat(q, numbLinePoints);
sin_theta_all = np.repeat(sin_theta, numbLinePoints);
cos_theta_all = np.repeat(cos_theta, numbLinePoints);

# position points on Poisson lines/segments
xxPP_all = xx0_all + p_all * cos_theta_all + q_all * uu * sin_theta_all;
yyPP_all = yy0_all + p_all * sin_theta_all - q_all * uu * cos_theta_all;
### END Simulate a Poisson point processes on each line ###END

### START Plotting ###START
# plot circle
t = np.linspace(0, 2 * np.pi, 200);
xp = r * np.cos(t);
yp = r * np.sin(t);
fig, ax = plt.subplots();
ax.plot(xx0 + xp, yy0 + yp, color='k');
plt.xlabel('x');
plt.ylabel('y');
plt.axis('equal');

# plot segments of Poisson line process
# need to create a list to plot the segments (probably a better way to do this)
segments = [[(xx1[i], yy1[i]), (xx2[i], yy2[i])] for i in range(numbLines)];
lc = mc.LineCollection(segments, colors='b')
ax.add_collection(lc)  # plot segments

# plot Poisson points on line process
plt.plot(xxPP_all, yyPP_all, 'r.');
###END Plotting END###


import math
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat
import scipy.optimize as opt

data = loadmat('data/ex5data1.mat')
x_train = data['X']
y_train = data['y']
x_val = data['Xval']
y_val = data['yval']
x_test = data['Xtest']
y_test = data['ytest']
#print(x_train.shape)
#print(y_train.shape)
#print(x_val.shape)
#print(y_val.shape)
#print(x_test.shape)
#print(y_test.shape)

x_train = np.insert(x_train, 0, np.ones(x_train.shape[0],), axis=1)
x_val = np.insert(x_val, 0, np.ones(x_val.shape[0],), axis=1)
x_test = np.insert(x_test, 0, np.ones(x_test.shape[0],), axis=1)
y_train = y_train[:,0]
y_val = y_val[:,0]
y_test = y_test[:,0]
#print(x_train)

def J(theta, x, y, λ):
	m = x.shape[0]
	tmp = (x @ theta) - y
	j = (tmp @ tmp) / np.float64(2 * m) + (λ / np.float64(2 * m)) * (theta[1:] @ theta[1:])
	return j

def gradient(theta, x, y, λ):
	m = x.shape[0]
	tmp = (x @ theta - y)
	grad = (x.transpose() @ tmp) / np.float64(m)
	tmp = (λ / np.float64(m)) * theta
	tmp[0] = 0
	grad += tmp
	return grad

def plotLine(slope, intercept):
    #Plot a line from slope and intercept
    axes = plt.gca()
    x_vals = np.array(axes.get_xlim())
    y_vals = intercept + slope * x_vals
    plt.plot(x_vals, y_vals, '-')

def learningCurveData(x_train, y_train, x_val, y_val, λ):
	m = x_train.shape[0]
	n = x_train.shape[1]
	theta = np.zeros((n,))
	training_error = np.zeros(m - 1)
	validation_error = np.zeros(m - 1)
	for i in range(1, m):
		x = x_train[:i,:]
		y = y_train[:i]
		fmin = opt.fmin_tnc(func=J, x0=theta, fprime=gradient, args=(x, y, λ))
		training_error[i - 1] = J(fmin[0], x, y, 0)
		validation_error[i - 1] = J(fmin[0], x_val, y_val, 0)
	return training_error, validation_error

def polyFeatures(x, p):
	m = x.shape[0]
	x_poly = np.zeros((m, p + 1))
	for i in range(p + 1):
		x_poly[:,i] = np.power(x, i)
	return x_poly

def featureScaling(data):
	m = data.shape[0]
	mean = data.sum(axis=0) / m
	std = data.std(axis=0)
	mean[0], std[0] = 0.0, 1.0
	normalized_data = (data - mean) / std
	return normalized_data, mean, std

def plotLearntFunc(theta, mean, std):
	delta = 0.25
	xrange = np.arange(-100.0, 100.0, delta)
	yrange = np.arange(-100.0, 100.0, delta)
	X, Y = np.meshgrid(xrange,yrange)
	print(X.shape)
	# F is one side of the equation, G is the other
	tmp = [0.0 for el in range(p + 1)]
	lhs = theta[0]
	for i in range(1, p + 1):
		tmp[i] = (np.power(X, i) - mean[i]) / std[i]
		lhs += theta[i] * tmp[i]
	F = lhs
	G = Y
	plt.contour(X, Y, (F - G), [0], colors=["k"])

def plotLearningCurve(x_train, y_train, x_val, y_val, λ):
	training_error, validation_error = learningCurveData(x_train, y_train, x_val, y_val, λ)
	plt.plot(training_error, 'b')
	plt.plot(validation_error, 'g')

p = 8
theta = np.zeros((p + 1,), dtype=np.float64)
#try different values of lamda to see how the learning curves vary
λ = 0
x_train_poly = polyFeatures(x_train[:,1], p)
x_val_poly = polyFeatures(x_val[:,1], p)
#mean-normalize x_train_poly & x_val_poly
x_train_poly_norm, mean_train, std_train = featureScaling(x_train_poly)
x_val_poly_norm = (x_val_poly - mean_train) / std_train

fmin = opt.fmin_tnc(func=J, x0=theta, fprime=gradient, args=(x_train_poly_norm, y_train, λ))
theta = fmin[0]

plt.plot(x_train[:,1], y_train,'rx')
plotLearntFunc(theta, mean_train, std_train)
#plotLearningCurve(x_train_poly_norm, y_train, x_val_poly_norm, y_val, λ)
#plotLine(fmin[0][1], fmin[0][0])
plt.show()


"""Workout for Statistical tests to estimate population proportions

These functions are created from using basic python libraries

The module contains useful functions for evaluating AB tests
1. one_prop_conf_interval
2. one_prop_hypothesis
3. two_props_diff_conf_interval
4. two_props_hypothesis

"""
import math
import numpy as np
import classical.workout.utils as utils

def one_prop_conf_interval(values: np.ndarray,
                           conf_level: float = 0.95) -> tuple:
    """calculates confidence interval for a proportion

    Args:
        values (np.array): sample values (values are of values 0 and 1)
        conf_level (float): confidence level

    Returns:
        tuple: lower and upper values of confidence interval
    """
    p = np.mean(values)
    n = len(values)
    se = math.sqrt(p*(1-p)/n)
    z_critical = utils.get_z_critical(conf_level)
    lower_ci = p - z_critical * se
    upper_ci = p + z_critical * se
    return (lower_ci, upper_ci)

def one_prop_hypothesis(values: np.ndarray,
                        null_val: float = 0,
                        alternative: str = "two-sided"):
    p = np.mean(values)
    n = len(values)
    se = math.sqrt(null_val*(1-null_val)/n)
    z_statistic = (p - null_val)/se
    p_value = utils.get_norm_pvalue(z_statistic, alternative)
    return (z_statistic, p_value)

def two_props_diff_conf_interval(values1: np.ndarray, values2: np.ndarray,
                                 conf_level: float) -> tuple:
    """Calculates the confidence interval for the diff between two proportions

    Args:
        values1 (np.array): sample 1 binary(0/1) values
        values2 (np.array): sample 2 binary(0/1) values
        conf_level (float): confidence level

    Returns:
        tuple: lower and upper values of confidence interval
    """
    p1 = np.mean(values1)
    p2 = np.mean(values2)
    p_diff = p1-p2
    n1 = len(values1)
    n2 = len(values2)
    SE = math.sqrt(((p1*(1-p1))/n1) + ((p2*(1-p2))/n2))
    z_critical = utils.get_z_critical(conf_level)
    lower = p_diff - z_critical*SE
    upper = p_diff + z_critical*SE
    return (lower, upper)

def two_props_hypothesis(values1: np.ndarray,
                         values2: np.ndarray,
                         alternative: str = "two-sided") -> tuple:
    """Perform two sided z test to comparing two proportions

    Args:
        values1 (np.array): sample 1 binary(0/1) values
        values2 (np.array): sample 2 binaray(0/1) values
        alternative (str, optional): two-sided/larger/smaller.
                                     Defaults to "two-sided".

    Returns:
        tuple: z_statistic and p value of the test
    """
    p1 = np.mean(values1)
    p2 = np.mean(values2)
    n1 = len(values1)
    n2 = len(values2)
    p_pool = (np.sum(values1) + np.sum(values2))/(n1+n2)
    se = math.sqrt((p_pool*(1-p_pool))/n1 + (p_pool*(1-p_pool))/n2)
    z_statistic = (p1-p2)/se
    p_value = utils.get_norm_pvalue(z_statistic, alternative)
    return (z_statistic, p_value)


import numpy as np
from matplotlib import use
use('TkAgg')
import matplotlib.pyplot as plt
from gradientDescentMulti import gradientDescentMulti
from normalEqn import normalEqn
from featureNormalize import featureNormalize
from featureNormalize import normEntry
# ================ Part 1: Feature Normalization ================

print('Loading data ...')

# Load Data
data = np.loadtxt('ex1data2.txt', delimiter=',')
X = data[:, :2]
y = data[:, 2]
m = y.T.size

# Print out some data points
print('First 10 examples from the dataset:')
print(np.column_stack( (X[:10], y[:10]) ))
input("Program paused. Press Enter to continue...")

# Scale features and set them to zero mean
print('Normalizing Features ...')

X, mu, sigma = featureNormalize(X)
print('[mu] [sigma]')
print(mu, sigma)

# Add intercept term to X
X = np.concatenate((np.ones((m, 1)), X), axis=1)

print('Running gradient descent ...')
# Choose some alpha value
alpha = 0.01
num_iters = 9000
# Init Theta and Run Gradient Descent
theta = np.zeros(3)
theta, J_history = gradientDescentMulti(X, y, theta, alpha, num_iters)
# Plot the convergence graph
plt.plot(J_history, '-b')

''' #Plot for some different alphas
plt.gca().set_color_cycle(['red', 'green', 'blue', 'yellow'])
for alpha in np.linspace(0.001, 0.6, 10):
    num_iters = 500
    # Init Theta and Run Gradient Descent
    theta = np.zeros(3)
    theta, J_history = gradientDescentMulti(X, y, theta, alpha, num_iters)
    # Plot the convergence graph
    plt.plot(J_history, '-b')
'''
plt.xlabel('Number of iterations')
plt.ylabel('Cost J')
plt.show()
input("Program paused. Press Enter to continue...")

# Display gradient descent's result
print('Theta computed from gradient descent: ')
print(theta)

# Estimate the price of a 1650 sq-ft, 3 br house
nPrice = normEntry([1, 1650, 3], mu, sigma)
price = np.array(nPrice).dot(theta)

print('Predicted price of a 1650 sq-ft, 3 br house')
print('(using gradient descent): ')
print(price)

input("Program paused. Press Enter to continue...")

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

# ====================== YOUR CODE HERE ======================
# Instructions: The following code computes the closed form
#               solution for linear regression using the normal
#               equations. You should complete the code in
#               normalEqn.m
#
#               After doing so, you should complete this code
#               to predict the price of a 1650 sq-ft, 3 br house.
#

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

# Load Data
data = np.loadtxt('ex1data2.txt', delimiter=',')
X = data[:, :2]
y = data[:, 2]
m = y.T.size

# Add intercept term to X
X = np.concatenate((np.ones((m,1)), X), axis=1)

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

# Display normal equation's result
print('Theta computed from the normal equations:')
print(' %s \n' % theta)

# Estimate the price of a 1650 sq-ft, 3 br house
price = np.array([1, 1650, 3]).dot(theta)

# ============================================================

print("Predicted price of a 1650 sq-ft, 3 br house ")
print('(using normal equations):\n $%f\n' % price)

input("Program paused. Press Enter to continue...")


from statistics import mean
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
import random

style.use('fivethirtyeight')

#xs = [1, 2, 3, 4, 5, 6]
#ys = [5, 4, 6, 4, 5, 7]

# plt.plot(xs, ys)
# plt.scatter(xs, ys)
# plt.show()

#xs = np.array([1, 2, 3, 4, 5, 6], dtype = np.float64)
#ys = np.array([5, 4, 6, 5, 6, 7], dtype = np.float64)

def create_dataset(number_of_datapoint, variance, step=2, correlation=False):
    val = 1
    ys = []
    for i in range(number_of_datapoint):
        y = val + random.randrange(-variance, variance)
        ys.append(y)
        if correlation and correlation == 'pos':
            val+=step
        elif correlation and correlation == 'neg':
            val-=step
    xs = [i for i in range(len(ys))]    # we can give the len(number_of_datapoint)
    return np.array(xs, dtype=np.float64), np.array(ys, dtype=np.float64)

def best_fit_slope_and_intercept(xs, ys):

    x_mean = mean(xs)
    y_mean = mean(ys)

    xy_means = (x_mean*y_mean)
    mean_of_xy = mean(xs*ys)

    x_mean_sq = x_mean*x_mean
    x_sq_mean = mean(xs*xs)

    m = ((xy_means - mean_of_xy)/ (x_mean_sq - x_sq_mean))
    b = y_mean - m*x_mean

    return m, b

def sqaured_error(ys_original, ys_line):
    return sum((ys_line-ys_original)**2)

def coefficient_of_determination(ys_original, ys_line):
    y_mean_line = [mean(ys_original) for y in ys_original]
    squared_error_of_regression_line = sqaured_error(ys_original, ys_line)
    sqaured_error_y_mean_line = sqaured_error(ys_original, y_mean_line)

    return 1 - (squared_error_of_regression_line / sqaured_error_y_mean_line)

xs, ys = create_dataset(40, 40, 2, correlation='neg')

m, b = best_fit_slope_and_intercept(xs, ys)
#print(m, b)

#for x in xs:
#    regression_line.append(m*x)+b)

regression_line = [(m*x)+b for x in xs]

predict_x = 8
predict_y = m*predict_x+b

# Coefficient of determination
r_sqaured = coefficient_of_determination(ys, regression_line)
print(r_sqaured)

plt.scatter(xs, ys)
plt.plot(xs, regression_line)
plt.scatter(predict_x, predict_y, s=100, color='g')
plt.show()

Linear Regression from Scratch/Linear Regression from Scratch.py


from sphericalquadpy.gausslegendre.gausslegendre import GaussLegendre
import pytest
from numpy import pi, inf

def test_icolerp():
    Q = GaussLegendre(order=2)

    assert Q.name() == "GaussLegendre Quadrature"

    assert Q.getmaximalorder() == inf

    xyz = Q.computequadpoints(10)
    w = Q.computequadweights(10)

    nq = Q.nqbyorder(10)

    assert len(w) == nq[1]

def test_weights():
    Q = GaussLegendre(order=10)
    assert abs(sum(Q.weights) - 4 * pi) < 1e-10

def test_invalid():
    Q = GaussLegendre(order=3)
    with pytest.raises(Exception):
        _ = Q.computequadpoints(234234234234)
    with pytest.raises(Exception):
        _ = Q.computequadweights(234234234234)


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

# Reading Data
data = pd.read_csv('AreaRent.csv')
print("DATASET \n",data)

# Collecting X and Y
X = data['Area'].values
Y = data['Rent'].values

# Mean X and Y
mean_x = np.mean(X)
mean_y = np.mean(Y)

# Total number of values
n = len(X)

# Using the formula to calculate m and c
numer = 0
denom = 0
for i in range(n):
    numer += (X[i] - mean_x) * (Y[i] - mean_y)
    denom += (X[i] - mean_x) ** 2
b1 = numer / denom
b0 = mean_y - (b1 * mean_x)

# Print coefficients
print("\nb1=" , b1 , "\nb0=" ,b0)

#Print Equation
print("Therefore, \nThe Linear Regression for given model is: y=",round(b1,3),"x+ (",round(b0,3),")")

#Finding Value of y
X1=input("Predicting Rent \n Enter Area(X): ")
y1= b1*float(X1) + b0
print("Area ", X1, "sq. ft has Rent of Rs. = ",y1)

# Plotting Values and Regression Line
max_x = np.max(X) + 5
min_x = np.min(X) - 5
# Calculating line values x and y
x = np.linspace(min_x, max_x)
y = b0 + b1 * x

# Ploting Line
plt.plot(x, y, c='#52b920', label='Regression Line')
# Ploting Scatter Points
plt.scatter(X, Y, c='#ef4423', label='Scatter Plot')
plt.xlabel('Area')
plt.ylabel('Rent')
plt.legend()
plt.show()


# Author: Jintao Huang
# Email: hjt_study@qq.com
# Date:

from numba import njit
import random
import math
from typing import Callable

@njit
def func(x: float) -> float:
    return math.sqrt(1 - x ** 2)

@njit
def HitorMiss(f: Callable[[float], float], n: int) -> float:
    """f: [0, 1]->[0, 1]"""
    k = 0  # 命中
    for _ in range(n):
        x = random.uniform(0, 1)
        y = random.uniform(0, 1)
        if y <= f(x):  # (x, y)在阴影处
            k += 1
    return k / n

pi = math.pi
print("精确解: %f" % pi)
#
print()
print("n 计算的pi值 误差")
for i in range(9):
    n = 10 ** i
    res = 4 * HitorMiss(func, n)
    print(n, res, abs(pi - res))

"""Out
精确解: 3.141593

n 计算的pi值 误差
1 4.0 0.8584073464102069
10 3.2 0.05840734641020706
100 3.16 0.018407346410207026
1000 3.188 0.04640734641020705
10000 3.1424 0.000807346410206744
100000 3.14836 0.006767346410206709
1000000 3.14018 0.0014126535897931447
10000000 3.1423656 0.0007729464102070871
100000000 3.14156932 2.3333589793228526e-05
"""


# Project Euler Problem 10
# 3/18/2013
# Copyright Shayne Hodge
# Find the sum of all the primes below two million.

import numpy

def sieve(start):
    if (start == 2):
        k = 2
        while (((k*start)-offset) <= (isprimelen-1)):
            isprime[(k*start) - offset] = 0
            k += 1
        return
    k = 3
    while (((k*start)-offset) <= (isprimelen-1)):
        isprime[(k*start) - offset] = 0
        k += 2
    return

def nextvalue(lastvalue):
    iindex = lastvalue - offset + 2
    while (iindex <= (end_value-offset)):
        if isprime[iindex] == 1:
            return (iindex + offset)
        else:
            iindex += 2
    return (end_value+1)

end_value = 2*10**6 - 1
sievelist = numpy.array(range(2, end_value+1),dtype='int64')
offset = sievelist[0]
isprime = numpy.array([1] * (end_value + 1 - offset),dtype='int64')
isprimelen = len(isprime)

sieve(2)
sieve(3)
knownprime = 5

while (knownprime <= end_value):
    sieve(knownprime)
    knownprime = nextvalue(knownprime)

primesum = sum(sievelist*isprime)

print('The sum of all primes less than', end_value+1, 'is', primesum, '.')


# -*- coding: utf-8 -*-#
'''
# Name:         Evaluation
# Description:  回归常用评价指标
# Author:       super
# Date:         2020/5/14
'''

import numpy as np
from sklearn import metrics

def MSE(y_true, y_pred):
    return np.average((y_true - y_pred) ** 2, axis=0)
    # return np.mean((y_pred - y_true) * (y_pred - y_true))

def RMSE(y_true, y_pred):
    return np.sqrt(np.average((y_true - y_pred) ** 2, axis=0))

def MAE(y_true, y_pred):
    return np.average(np.abs(y_pred - y_true), axis=0)

def MAPE(y_true, y_pred):
    return np.average(np.abs((y_pred - y_true) / y_true), axis=0) * 100

def SMAPE(y_true, y_pred):
    return 2.0 * np.average(np.abs(y_pred - y_true) / (np.abs(y_pred) + np.abs(y_true)), axis=0) * 100

if __name__ == '__main__':
    y_pred = np.array([1.0, 3.3, 2.0, 7.9, 5.5, 6.4, 2.0])
    y_true = np.array([2.0, 3.0, 2.5, 1.0, 4.0, 3.2, 3.0])
    print(metrics.mean_squared_error(y_true=y_true, y_pred=y_pred))
    print(MSE(y_true=y_true, y_pred=y_pred))

    print(np.sqrt(metrics.mean_squared_error(y_true=y_true, y_pred=y_pred)))
    print(RMSE(y_true=y_true, y_pred=y_pred))

    print(metrics.mean_absolute_error(y_true=y_true, y_pred=y_pred))
    print(MAE(y_true=y_true, y_pred=y_pred))


import numpy as np

def procrustes(X, Y, scaling=True, reflection='best'):
    """
    A port of MATLAB's `procrustes` function to Numpy.

    Procrustes analysis determines a linear transformation (translation,
    reflection, orthogonal rotation and scaling) of the points in Y to best
    conform them to the points in matrix X, using the sum of squared errors
    as the goodness of fit criterion.

        d, Z, [tform] = procrustes(X, Y)

    Inputs:
    ------------
    X, Y
        matrices of target and input coordinates. they must have equal
        numbers of  points (rows), but Y may have fewer dimensions
        (columns) than X.

    scaling
        if False, the scaling component of the transformation is forced
        to 1

    reflection
        if 'best' (default), the transformation solution may or may not
        include a reflection component, depending on which fits the data
        best. setting reflection to True or False forces a solution with
        reflection or no reflection respectively.

    Outputs
    ------------
    d
        the residual sum of squared errors, normalized according to a
        measure of the scale of X, ((X - X.mean(0))**2).sum()

    Z
        the matrix of transformed Y-values

    tform
        a dict specifying the rotation, translation and scaling that
        maps X --> Y

    """

    n,m = X.shape
    ny,my = Y.shape

    muX = X.mean(0)
    muY = Y.mean(0)

    X0 = X - muX
    Y0 = Y - muY

    ssX = (X0**2.).sum()
    ssY = (Y0**2.).sum()

    # centred Frobenius norm
    normX = np.sqrt(ssX)
    normY = np.sqrt(ssY)

    # scale to equal (unit) norm
    X0 /= normX
    Y0 /= normY

    if my < m:
        Y0 = np.concatenate((Y0, np.zeros(n, m-my)),0)

    # optimum rotation matrix of Y
    A = np.dot(X0.T, Y0)
    U,s,Vt = np.linalg.svd(A,full_matrices=False)
    V = Vt.T
    T = np.dot(V, U.T)

    if reflection is not 'best':

        # does the current solution use a reflection?
        have_reflection = np.linalg.det(T) < 0

        # if that's not what was specified, force another reflection
        if reflection != have_reflection:
            V[:,-1] *= -1
            s[-1] *= -1
            T = np.dot(V, U.T)

    traceTA = s.sum()

    if scaling:

        # optimum scaling of Y
        b = traceTA * normX / normY

        # standarised distance between X and b*Y*T + c
        d = 1 - traceTA**2

        # transformed coords
        Z = normX*traceTA*np.dot(Y0, T) + muX

    else:
        b = 1
        d = 1 + ssY/ssX - 2 * traceTA * normY / normX
        Z = normY*np.dot(Y0, T) + muX

    # transformation matrix
    if my < m:
        T = T[:my,:]
    c = muX - b*np.dot(muY, T)

    #transformation values
    tform = {'rotation':T, 'scale':b, 'translation':c}

    return d, Z, tform


from numpy import random, pi

Ntrials, Nhits = 1_000_000, 0
for n in range(Ntrials):
    x, y = random.uniform(-1, 1, 2) # draw 2 samples, each uniformly distributed over (-1,1)
    if (x**2 + y**2)**2 <= 2*abs(x*y):
        Nhits += 1
print("Monte Carlo estimator of Pi: %.5f" % (4 * Nhits / Ntrials))


import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 100, 100)
y1 = np.power(x, 0.75)
y2 = np.power(x, 0.5)

plt.plot(x, y1)
plt.plot(x, y2)

plt.grid(True)
plt.savefig('../../img/question_4_plots/a.png')


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

# Program do znajdowania pierwiastków równania
# x**3 + x**2 - 3*x - 2 = 0
# w przedziałach [-3, -1] i [-1, 1]
# metodą bisekcji

import numpy as np

def f(u):
    '''Oblicza wartość funkcji wielomianu z zadania'''
    wartosc = u ** 3 + u ** 2 - 3 * u - 2

    return wartosc

print "Program do znajdowania pierwiastków równania\n"
print "x**3  +  x**2  - 3*x  - 2 = 0\n"
print "w przedziałach [-3, -1] i [-1, 1]"
print "metodą bisekcji."

# wartości ograniczeń przedziału
a = -3.0
b = -1.0
c = 1.0

N = int(raw_input("\nPodaj liczbę kroków "))

# obliczenie pierwiastków w przedziale [-3, -1]
# dolna granica przedziału
d = a

# górna granica przedziału
u = b

for licz in range (0,2):
    for k in range(0, N):
        # środek przedziału
        m = (d + u) / 2

        # oblicznie wartości funkcji wielomianu dla m i d
        wart_m = f(m)
        wart_d = f(d)

        # określenie znaku otrzymanych wartości
        znak_wartm = np.sign(wart_m)
        znak_wartd = np.sign(wart_d)

        #sprawdzenie czy wartość funkcji dla argumentu z środka przedziału jest równa 0
        # sprawdzenie czy wartości na końcach przedziałów mają ten sam znak
        # czyli nie funkcja w tym przedziale nie przechodzi przez osi OX
        if znak_wartm == znak_wartd:
            d = m

        # sprawdzenie czy wartości na końcach przedziałów mają przeciwne znaki
        # czyli funkcja w tym przedziale przechodzi przez osi OX
        if znak_wartm == ( znak_wartd * (-1) ):
            u = m

    #przedział, w którym znajduje się pierwiastek
    print "\nWyznaczony przedział, w którym znajduje się pierwiastek"
    print "[d, u] = ", "[", d, ",", u, "]"

    #wyznaczenie pierwiastka
    pierw = (d + u)/2
    wart_p= f(pierw)
    print "\nPierwiastek wynosi ", pierw
    print "f(",pierw,") = ", wart_p

    #zmian granic przedziału dla obliczenia pierwiastka w przedziale [-1,1]
    d = b
    u = c

#wyliczenie miary błędu
bl_1 = (b-a)/(2**N)
bl_2 = (c-b)/(2**N)

print "\nBłąd wyznaczenia pierwiastka w przedziale [-3,-1] wynosi ", bl_1
print "Błąd wyznaczenia pierwiastka w przedziale [-1,1]  wynosi ", bl_2


import logging
from functools import partial
from typing import Tuple, Callable

import numpy as np
from numpy.linalg import pinv
from src.local_optimization.local_optimizer import LocalOptimizer
from src.termination import check_n_iter, check_absolute_cost, check_n_iter_without_improvement
from src.math import gradient
from src.loss import mse

logger = logging.getLogger(__name__)

TERMINATION_CHECKS = (
    partial(check_n_iter, threshold=500),
    partial(check_absolute_cost, threshold=1e-6),
    partial(check_n_iter_without_improvement, threshold=20),
)

class LevenbergMarquardt(LocalOptimizer):
    """
    Levenberg-Marquardt (LMA) optimization method.

    Levenberg-Marquardt is damped Gauss-Newton method where increment vector is rotated towards the direction of
    steepest descent. Amount of this rotation is determined by damping factor which is changed during iteration.
    If cost decreases, the variables are updated and damping factor is decreased, making LMA to work more like
    standard Gauss-Newton. If cost increases, the variables are not updated and damping factor is increased,
    making LMA to work more like gradient descent.
    """

    def __init__(self,
                 f_err: Callable,
                 damping_increase_factor=2.0,
                 damping_decrease_factor=2.0,
                 termination_checks=TERMINATION_CHECKS
                 ):
        """
        @param f_err: Function to calculate errors: errors = f_err(x). cost is then calculated as MSE(errors).
        @param damping_increase_factor: Scale factor to increase damping factor (> 1).
        @param damping_decrease_factor: Scale factor to decrease damping factor (> 1).
        @param termination_checks: See LocalOptimizer.
        """
        self.f_err = f_err
        self.weights = None
        self.damping_increase_factor = damping_increase_factor
        self.damping_decrease_factor = damping_decrease_factor
        self.damping_factor = None
        super().__init__(self.f_cost, termination_checks)

    def update(self, x, iter_round, cost) -> Tuple[np.ndarray, float]:
        x_delta = self._calculate_update_delta(x)
        x_candidate = x - x_delta
        cost_candidate = self.f_cost(x_candidate)
        if cost_candidate < cost:
            x = x_candidate
            cost = cost_candidate
            self.damping_factor = self.damping_factor / self.damping_decrease_factor
            logger.debug(f"Cost decreased; decrease damping factor and update variables")
        else:
            self.damping_factor = self.damping_increase_factor * self.damping_factor
            logger.debug(f"Cost increased; increase damping factor and don't update variables")
        logger.debug(f"Cost {cost:0.3f}; damping factor {self.damping_factor:0.3f}")

        return x, cost

    def _calculate_update_delta(self, x) -> np.ndarray:
        errors = self.f_err(x)
        jac = gradient(x, self.f_err)
        jj = jac.T @ jac
        if self.damping_factor is None:
            self.damping_factor = np.max(np.diag(jj))
        correction = np.diag(self.damping_factor * np.diag(jj))
        return pinv(jj + correction) @ jac.T @ errors

    def f_cost(self, x):
        return mse(self.f_err(x))


import numpy as np  # do obliczeń trygonometrycznych oraz generowania argumentów funkcji do stworzenia wykresu
import sympy as sp  # do tworzenia wzorów funkcji z parametrem oraz obliczeń pochodnych
from horner import horner

def wartosc_funkcji(arg_x, wybor):
    """Zwracanie wartości wybranej funkcji dla argumentu x

            Parametry
            ----------
            arg_x : float
                wartosc argumentu x
            wybor  : String
                wybór funkcji A, B, C, D lub E
            Dane wyjsciowe
            -------
            wart_fun : float
                wartość funkcji dla argumentu x
    """
    if wybor == "A":    # A dla funkcji wielomianowej
        wart_fun = horner([5, 2, -1, 5], arg_x)  # obliczenie wartosci schematem hornera
    elif wybor == "B":  # B dla funkcji trygonometrycznej
        wart_fun = 5 * np.cos(arg_x) - 3 * np.sin(arg_x)
    elif wybor == "C":  # C dla modułu
        wart_fun = abs(arg_x - 5)
    elif wybor == "D":  # D dla liniowej
        wart_fun = arg_x - 5
    elif wybor == "E":  # D dla liniowej
        wart_fun = abs(np.cos(arg_x) - 0.5)
    else:
        print("Podano nieprawidlowa wartosc. Wybierz a, b, c lub d")
        wart_fun = None
    return wart_fun     # zwróc wartość funkcji dla konkretnego argumentu x

def wzor_funkcji(wybor):
    """Zwracanie wzoru funkcji z parametrem x

            Parametry
            ----------
            wybor  : String
                wybór funkcji A, B, C, D lub E
            Dane wyjsciowe
            -------
            wzor_fun : Symbol
                wzór funkcji zapisany ze zmienną x
    """
    arg_x = sp.Symbol('x')
    if wybor == "A":  # A dla funkcji wielomianowej
        wzor_fun = horner([5, 2, -1, 5], arg_x)  # obliczenie wartosci schematem hornera
    elif wybor == "B":  # B dla funkcji trygonometrycznej
        wzor_fun = 5 * sp.cos(arg_x) - 3 * sp.sin(arg_x)
    elif wybor == "C":  # C dla modułu
        wzor_fun = abs(arg_x - 5)
    elif wybor == "D":  # D dla liniowej
        wzor_fun = arg_x - 5
    elif wybor == "E":  # D dla liniowej
        wzor_fun = abs(sp.cos(arg_x) - 0.5)
    else:
        print("Podano nieprawidlowa wartosc. Wybierz a, b, c lub d")
        wzor_fun = None
    return wzor_fun     # zwroc wzór funkcji


#!/usr/bin/env python3
"""
Clustering Module
"""
import numpy as np

def maximization(X, g):
    """
    Calculates the maximization step in the EM algorithm for a GMM:

    X is a numpy.ndarray of shape (n, d) containing the data set
    g is a numpy.ndarray of shape (k, n) containing the posterior
    probabilities for each data point in each cluster
    You may use at most 1 loop

    Returns: pi, m, S, or None, None, None on failure
        pi is a numpy.ndarray of shape (k,) containing the updated priors for
        each cluster
        m is a numpy.ndarray of shape (k, d) containing the updated centroid
        means for each cluster
        S is a numpy.ndarray of shape (k, d, d) containing the updated
        covariance matrices for each cluster
    """
    if not isinstance(X, np.ndarray) or len(X.shape) != 2:
        return None, None, None
    if not isinstance(g, np.ndarray) or len(g.shape) != 2:
        return None, None, None
    if X.shape[0] != g.shape[1]:
        return None, None, None

    n, d = X.shape
    k, _ = g.shape

    if not np.isclose(np.sum(g, axis=0), np.ones((n, ))).all():
        return None, None, None
    pi, m, s = np.zeros((k,)), np.zeros((k, d)), np.zeros((k, d, d))

    for i in range(k):
        m[i] = np.dot(g[i], X) / np.sum(g[i])
        xmm = X - m[i]
        s[i] = np.dot(g[i] * xmm.T, xmm) / np.sum(g[i])
        pi[i] = np.sum(g[i]) / n
    return pi, m, s


'''
    Badea Adrian Catalin, grupa 334

    Sa se genereze prin doua metode variabila Beta(2, 4).
    Sa se genereze histogramele asociate celor doua metode

'''
from scipy.stats import gamma
from scipy.stats import uniform
import numpy as np

import matplotlib as mpl
import matplotlib.pyplot as plt

def plot_hist(b, title):

    pl, ax = plt.subplots(1, 1)

    ax.hist(b, density=True, histtype='stepfilled', alpha=0.2)

    ax.set_title(title)

    plt.show()

def plot_hist_algorithm(b, title):

    b_np = np.array(b)

    minim = np.min(b_np)
    maxim = np.max(b_np)
    k = 40

    x = np.linspace(minim, maxim, k)
    f = np.zeros(k)

    for i in range(0, len(b)):
        for j in range(0, k - 1):
            if x[j] <= b[i] and b[i] <= x[j + 1]:
                f[j] += 1

    r = np.zeros(k)
    for j in range(0, k - 1):
        r[j] = f[j] / len(b)

    pl, ax = plt.subplots(1, 1)

    ax.plot(x, r)

    ax.set_title(title)

def gamma_va(a, nr_sub):

    pl, ax = plt.subplots(1, 1)

    x = np.linspace(gamma.ppf(0.001, a), gamma.ppf(0.999, a), nr_sub)

    ax.plot(x, gamma.pdf(x, a), 'r-', lw=3, alpha=0.6, label='gamma pdf')

    ax.set_title('Gamma variable from 0 to 1 with a = {}'.format(a))

    return gamma.pdf(x, a)

def beta_va(a, b, nr_samples):

    gamma_va(a, nr_samples)
    gamma_va(b, nr_samples)

    g1 = gamma.rvs(a, size = nr_samples)

    g2 = gamma.rvs(b, size = nr_samples)

    b = []
    for i in range(0, nr_samples):
        if g1[i] + g2[i] > 0:
            b.append(g1[i] / (g1[i] + g2[i]))

    print('Beta from Gamma:')

    print('Mean: {}'.format(np.mean(np.array(b))))

    print('Standard deviation: {}'.format(np.std(np.array(b))))

    plot_hist(b, 'Beta calculated from gamma distributions')

    plot_hist_algorithm(b, 'Beta histogram from gamma')

def beta_u(a, b, nr_samples):

    n = a + b - 1
    b = []
    for i in range(0, nr_samples):

        u = uniform.rvs(size = n)
        u = sorted(u)
        b.append(u[a - 1])

    print('Beta from uniform:')

    print('Mean is {}'.format(np.mean(np.array(b))))

    print('Standard deviation is {}'.format(np.std(np.array(b))))

    plot_hist(b, 'Beta calculated from uniform distributions')

    plot_hist_algorithm(b, 'Beta histogram from uniform')

if __name__ == '__main__':

    a, b = 2, 4

    nr_samples = 1000000

    mpl.style.use('seaborn')

    print('Generation of Beta variable using Uniform and Gamma random variables for {} samples.'.format(nr_samples))

    print('Coefficients : a = {} and b = {}'.format(a, b))

    print('Theorical mean is a / (a + b) ')
    print('Expected mean : {}'.format(a / (a + b)))

    print('Theoretical variance is a * b / ((a + b) ^ 2 * (a + b + 1)')

    var = a * b / ((a + b) ** 2 * (a + b + 1))

    print('Expected variance: {}'.format(var))
    print('Expected standard deviation: {}'.format(np.sqrt(var)))

    beta_va(a, b, nr_samples)

    beta_u(a, b, nr_samples)


'''
Python Basics with Numpy
This is a brief introduction to Python.
The script uses Python 3.

The script below is to get familiar with:
1. Be able to use iPython Notebooks
2. Be able to use numpy functions and numpy matrix/vector operations
3. Understand the concept of "broadcasting"
4. Be able to vectorize code
Let's get started!
'''

#==============================================================================================================
#1 - Building basic functions with numpy
#==============================================================================================================

'''
Numpy is the main package for scientific computing in Python. It is maintained by a large community (www.numpy.org).Several key numpy functions such as np.exp, np.log, and np.reshape are used everyday in ML and DL.
'''
#------------------------------------------
# 1.1 - sigmoid function, np.exp()
#------------------------------------------
'''Before using np.exp(), we will use math.exp() to implement the sigmoid function. we will then see why np.exp() is preferable to math.exp().
Exercise: Build a function that returns the sigmoid of a real number x. Use math.exp(x) for the exponential function.
Reminder: sigmoid(x)=11+e−xsigmoid(x)=11+e−x is sometimes also known as the logistic function. It is a non-linear function used not only in Machine Learning (Logistic Regression), but also in Deep Learning.

To refer to a function belonging to a specific package we could call it using package_name.function().
'''

import math

def basic_sigmoid(x):
    """
    Compute sigmoid of x.

    Arguments:
    x -- A scalar

    Return:
    s -- sigmoid(x)
    """

    s = 1 / (1 + math.exp(-x))
    return s

#basic_sigmoid(3)	#0.9525741268224334
'''
Actually, we rarely use the "math" library in deep learning because the inputs of the functions are real numbers. In deep learning we mostly use matrices and vectors. This is why numpy is more useful.
### One reason why we use "numpy" instead of "math" in Deep Learning ###

'''
#Uncomment and run this line to see the error
#x = [1, 2, 3]
#basic_sigmoid(x)
'''
In fact, if x=(x1,x2,...,xn)x=(x1,x2,...,xn) is a row vector then np.exp(x)np.exp(x) will apply the exponential function to every element of x. The output will thus be: np.exp(x)=(ex1,ex2,...,exn)np.exp(x)=(ex1,ex2,...,exn)
'''
import numpy as np

# example of np.exp
x = np.array([1, 2, 3])
print(np.exp(x)) # result is (exp(1), exp(2), exp(3))

'''
Furthermore, if x is a vector, then a Python operation such as s=x+3s=x+3 or s=1xs=1x will output s as a vector of the same size as x.
'''
# example of vector operation
x = np.array([1, 2, 3])
print (x + 3)
#[4 5 6]
'''Any time we need more info on a numpy function, look at the official documentation or write np.exp? (for example) to get quick access to the documentation.
'''
import numpy as np # this means we can access numpy functions by writing np.function() instead of numpy.function()

def sigmoid(x):
    """
    Compute the sigmoid of x

    Arguments:
    x -- A scalar or numpy array of any size

    Return:
    s -- sigmoid(x)
    """

    s = 1 / (1 + np.exp(-x) )

    return s

#x = np.array([1, 2, 3])
#sigmoid(x)

#------------------------------------------
# 1.2 - Sigmoid gradient
#------------------------------------------
'''
we will need to compute gradients to optimize loss functions using backpropagation. Let's code our first gradient function.
Exercise: Implement the function sigmoid_grad() to compute the gradient of the sigmoid function with respect to its input x.
The formula is:

sigmoid_derivative(x)=σ′(x)=σ(x)(1−σ(x))
'''

def sigmoid_derivative(x):
    """
    Compute the gradient (also called the slope or derivative) of the sigmoid function with respect to its input x.
    we can store the output of the sigmoid function into variables and then use it to calculate the gradient.

    Arguments:
    x -- A scalar or numpy array

    Return:
    ds -- wer computed gradient.
    """

    s = sigmoid(x)
    ds = s*(1-s)

    return ds

#x = np.array([1, 2, 3])
#print ("sigmoid_derivative(x) = " + str(sigmoid_derivative(x)))

#------------------------------------------
# 1.3 - Reshaping arrays
#------------------------------------------
'''
Two common numpy functions used in deep learning are np.shape and np.reshape().
X.shape is used to get the shape (dimension) of a matrix/vector X.
X.reshape(...) is used to reshape X into some other dimension.
For example, in computer science, an image is represented by a 3D array of shape (length,height,depth=3)(length,height,depth=3). However, when we read an image as the input of an algorithm we convert it to a vector of shape (length∗height∗3,1)(length∗height∗3,1). In other words, we "unroll", or reshape, the 3D array into a 1D vector.

Let us implement image2vector() that takes an input of shape (length, height, 3) and returns a vector of shape (length*height*3, 1). For example, if we would like to reshape an array v of shape (a, b, c) into a vector of shape (a*b,c) we would do:
v = v.reshape((v.shape[0]*v.shape[1], v.shape[2])) # v.shape[0] = a ; v.shape[1] = b ; v.shape[2] = c
'''

def image2vector(image):
    """
    Argument:
    image -- a numpy array of shape (length, height, depth)

    Returns:
    v -- a vector of shape (length*height*depth, 1)
    """

    v = image.reshape(image.shape[0]*image.shape[1]*image.shape[2],1)

    return v

# This is a 3 by 3 by 2 array, typically images will be (num_px_x, num_px_y,3) where 3 represents the RGB values
image = np.array([[[ 0.67826139,  0.29380381],
        [ 0.90714982,  0.52835647],
        [ 0.4215251 ,  0.45017551]],

       [[ 0.92814219,  0.96677647],
        [ 0.85304703,  0.52351845],
        [ 0.19981397,  0.27417313]],

       [[ 0.60659855,  0.00533165],
        [ 0.10820313,  0.49978937],
        [ 0.34144279,  0.94630077]]])

print ("image2vector(image) = " + str(image2vector(image)))

#------------------------------------------
# 1.4 - Normalizing rows
#------------------------------------------
'''
Another common technique we use in Machine Learning and Deep Learning is to normalize our data. It often leads to a better performance because gradient descent converges faster after normalization. Here, by normalization we mean changing x to x/∥x∥ (dividing each row vector of x by its norm).

we can divide matrices of different sizes and it works fine: this is called broadcasting
Let us implement normalizeRows() to normalize the rows of a matrix. After applying this function to an input matrix x, each row of x should be a vector of unit length (meaning length 1).
'''

def normalizeRows(x):
    """
    Implement a function that normalizes each row of the matrix x (to have unit length).

    Argument:
    x -- A numpy matrix of shape (n, m)

    Returns:
    x -- The normalized (by row) numpy matrix. we are allowed to modify x.
    """

    # Compute x_norm as the norm 2 of x. Use np.linalg.norm(..., ord = 2, axis = ..., keepdims = True)
    x_norm = np.linalg.norm(x,axis=1,keepdims=True)

    # Divide x by its norm.
    x = x / x_norm

    return x

x = np.array([
    [0, 3, 4],
    [1, 6, 4]])
print("normalizeRows(x) = " + str(normalizeRows(x)))

'''
Note: In normalizeRows(), we can try to print the shapes of x_norm and x, and then rerun the assessment. we'll find out that they have different shapes. This is normal given that x_norm takes the norm of each row of x. So x_norm has the same number of rows but only 1 column. So how did it work when we divided x by x_norm? This is called broadcasting and we'll talk about it now!
'''
#------------------------------------------
# 1.5 - Broadcasting and the softmax function
#------------------------------------------
'''
A very important concept to understand in numpy is "broadcasting". It is very useful for performing mathematical operations between arrays of different shapes. For the full details on broadcasting, we can read the official broadcasting documentation.
Let us implement a softmax function using numpy. we can think of softmax as a normalizing function used when wer algorithm needs to classify two or more classes.
Read more about this at https://en.wikipedia.org/wiki/Softmax_function
'''

def softmax(x):
    """Calculates the softmax for each row of the input x.

    wer code should work for a row vector and also for matrices of shape (n, m).

    Argument:
    x -- A numpy matrix of shape (n,m)

    Returns:
    s -- A numpy matrix equal to the softmax of x, of shape (n,m)
    """

    # Apply exp() element-wise to x. Use np.exp(...).
    x_exp = np.exp(x)

    # Create a vector x_sum that sums each row of x_exp. Use np.sum(..., axis = 1, keepdims = True).
    x_sum = np.sum(x_exp,axis=1,keepdims=True)

    # Compute softmax(x) by dividing x_exp by x_sum. It should automatically use numpy broadcasting.
    s = x_exp/x_sum

    return s

x = np.array([
    [9, 2, 5, 0, 0],
    [7, 5, 0, 0 ,0]])
print("softmax(x) = " + str(softmax(x)))
'''
If we print the shapes of x_exp, x_sum and s above and rerun the assessment cell, we will see that x_sum is of shape (2,1) while x_exp and s are of shape (2,5). x_exp/x_sum works due to python broadcasting.
'''

#==============================================================================================================
# 2) Vectorization
#==============================================================================================================
'''
In deep learning, we deal with very large datasets. Hence, a non-computationally-optimal function can become a huge bottleneck in wer algorithm and can result in a model that takes ages to run. To make sure that wer code is computationally efficient, we will use vectorization. Below are a few examples that demonstarte this
'''
import time

x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]

### CLASSIC DOT PRODUCT OF VECTORS IMPLEMENTATION ###
tic = time.process_time()
dot = 0
for i in range(len(x1)):
    dot+= x1[i]*x2[i]
toc = time.process_time()
print ("dot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

### CLASSIC OUTER PRODUCT IMPLEMENTATION ###
tic = time.process_time()
outer = np.zeros((len(x1),len(x2))) # we create a len(x1)*len(x2) matrix with only zeros
for i in range(len(x1)):
    for j in range(len(x2)):
        outer[i,j] = x1[i]*x2[j]
toc = time.process_time()
print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

### CLASSIC ELEMENTWISE IMPLEMENTATION ###
tic = time.process_time()
mul = np.zeros(len(x1))
for i in range(len(x1)):
    mul[i] = x1[i]*x2[i]
toc = time.process_time()
print ("elementwise multiplication = " + str(mul) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

### CLASSIC GENERAL DOT PRODUCT IMPLEMENTATION ###
W = np.random.rand(3,len(x1)) # Random 3*len(x1) numpy array
tic = time.process_time()
gdot = np.zeros(W.shape[0])
for i in range(W.shape[0]):
    for j in range(len(x1)):
        gdot[i] += W[i,j]*x1[j]
toc = time.process_time()
print ("gdot = " + str(gdot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]

### VECTORIZED DOT PRODUCT OF VECTORS ###
tic = time.process_time()
dot = np.dot(x1,x2)
toc = time.process_time()
print ("dot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

### VECTORIZED OUTER PRODUCT ###
tic = time.process_time()
outer = np.outer(x1,x2)
toc = time.process_time()
print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

### VECTORIZED ELEMENTWISE MULTIPLICATION ###
tic = time.process_time()
mul = np.multiply(x1,x2)
toc = time.process_time()
print ("elementwise multiplication = " + str(mul) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

### VECTORIZED GENERAL DOT PRODUCT ###
tic = time.process_time()
dot = np.dot(W,x1)
toc = time.process_time()
print ("gdot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")

'''
As you may have noticed, the vectorized implementation is much cleaner and more efficient. For bigger vectors/matrices, the differences in running time become even bigger.
Note that np.dot() performs a matrix-matrix or matrix-vector multiplication. This is different from np.multiply() and the * operator (which is equivalent to  .* in Matlab/Octave), which performs an element-wise multiplication.
'''
#------------------------------------------
# 2.1 Implement the L1 and L2 loss functions
#------------------------------------------
'''
Let us implement the numpy vectorized version of the L1 loss. we may find the function abs(x) (absolute value of x) useful.
The loss is used to evaluate the performance of wer model. The bigger wer loss is, the more different wer predictions (ŷ y^) are from the true values (y). In deep learning, we use optimization algorithms like Gradient Descent to train our model and to minimize the cost.
L1 loss is defined as:
L1(ŷ ,y)=∑i=0m|y(i)−ŷ (i)
'''

def L1(yhat, y):
    """
    Arguments:
    yhat -- vector of size m (predicted labels)
    y -- vector of size m (true labels)

    Returns:
    loss -- the value of the L1 loss function defined above
    """

    loss = np.sum(abs(yhat-y))

    return loss

yhat = np.array([.9, 0.2, 0.1, .4, .9])
y = np.array([1, 0, 0, 1, 1])
print("L1 = " + str(L1(yhat,y)))

'''
Let us also implement the numpy vectorized version of the L2 loss. There are several way of implementing the L2 loss but we may find the function np.dot() useful. As a reminder, if x=[x1,x2,...,xn]x=[x1,x2,...,xn], then np.dot(x,x) = ∑nj=0x2j∑j=0nxj2.
L2 loss is defined as
L2(ŷ ,y)=∑i=0m(y(i)−ŷ (i))2
'''

def L2(yhat, y):
    """
    Arguments:
    yhat -- vector of size m (predicted labels)
    y -- vector of size m (true labels)

    Returns:
    loss -- the value of the L2 loss function defined above
    """

    loss = np.sum(np.dot(yhat-y,yhat-y))
    #Also possible loss = np.sum((yhat-y)**2)

    return loss

yhat = np.array([.9, 0.2, 0.1, .4, .9])
y = np.array([1, 0, 0, 1, 1])
print("L2 = " + str(L2(yhat,y)))

# BONUS :  A quick reshape hack is to use -1 as the parameter
a = np.random.rand(3,2,2,3)
b = a.reshape(3,-1).T
'''
The shape of b is converted to (3,12). The number of columns is calculated automatically.
'''


'''
This problem was asked by Facebook.

Given a stream of elements too large to store in memory, pick a random element from the stream with
uniform probability.

SOLUTION: Reservoir Sampling:
    probability of selecting the 1st element after seeing only one element 100%
    probability of selecting the 2nd element after seeing only two elements 1/2
    probability of selecting the 3rd element after seeing only three elements 1/3
    probability of selecting the 4th element after seeing only four elements 1/4
    probability of selecting the 5th element after seeing only five elements 1/5

    Proof uniform sampling
    so probability of selecting the 1st element after seeing five elements is:
    1 * (1 - 1/2) * (1 - 1/3) * (1 - 1/4) * (1 - 1/5)
  = 1 * 1/2 * 2/3 * 3/4 * 4/5 = 1/5
'''

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

def uniform_sample(big_stream, printAction=True):
    random_element = None

    for index, element in enumerate(big_stream):

        if index == 0:
            if printAction:
                print("picked element {} from stream of {} elements".format(element, index + 1))
            random_element = element
        elif random.randint(1, index+1) == 1:
            if printAction:
                print("picked element {} from stream of {} elements".format(element, index + 1))
            random_element = element

    return random_element

def create_hist(big_stream, no_of_samples = 5000):
    hist = []

    for i in range(no_of_samples):
        hist.append(uniform_sample(big_stream, printAction=False))

    plt.hist(np.array(hist), align='left')
    plt.ylabel('Probability')
    plt.show()

if __name__ == '__main__':
    random.seed(1)
    big_stream = [1, 2, 3, 4, 5, 6]
    #print(uniform_sample(big_stream))
    create_hist(big_stream, no_of_samples=10000)


#!/usr/bin/env
# -*- coding: utf-8 -*-
'''
Script Name     : Gauss_Newton
Author          : Charles Young
Python Version  : Python 3.6.3
Requirements    : (Please check document: requirements.txt or use command "pip install -r requirements.txt")
Date     		: 2018-11-05
'''

import numpy as np
import os
import sys
import argparse
from scipy import io
import matplotlib.pyplot as plt

# If run in Windows, comment this line.
# plt.switch_backend('agg')

def get_args():
	parser = argparse.ArgumentParser()
	parser.add_argument('--maxits', default = 256, type = np.int, nargs = '?', help = "Maximum number of iterations of the algorithm to perform. Default 256.")
	parser.add_argument('--seed', dest = 'random_seed', default = 0, type = np.int, nargs = '?', help = "Random seed. Default 0")
	parser.add_argument('--num_init', default = 10, type = np.int, nargs = '?', help = "Number of initial guesses. Default 10.")
	parser.add_argument('--orth', dest = 'orthogonal', action = 'store_const', const = True, help = "Whether to orthogonalize the initial guesses.")
	parser.add_argument('--result', dest = 'save_result', action = 'store_const', const = True, help = "Whether to save the results.")
	parser.add_argument('--legend', dest = 'show_legend', action = 'store_const', const = True, help = "Whether to show the legend in error graph.")
	parser.add_argument('--plot', dest = 'show_graph', action = 'store_const', const = True, help = "Whether to show the error graph.")
	return parser.parse_args()

def calc_err(X, y, R):
	# Calculate the standard deviation.
	err = y - np.dot(R, X)
	return np.trace(np.dot(err.T, err))

def solve(X, y, R0, tol = 1e-10, maxits = 256):
	"""Gauss-Newton algorithm.
	Parameters
	----------
	X : ndarray
		Original vectors.
	y : ndarray
		Target vectors after rotation.
	R0 : ndarray
		Initial guesses or starting estimates.
	tol : float
	    Tolerance threshold. The problem is considered solved when this value
	    becomes larger than the change of the correction vector.
	    Defaults to 1e-10.
	maxits : int
	    Maximum number of iterations of the algorithm to perform.
	    Defaults to 256.
	Return
	------
	Rn : ndarray
	    Resultant values. In this problem, the rotation matrix.
	err : float
		The standard deviation.
	its : int
	    Number of iterations performed.
	errs : list
		The standard deviations during iteration.
	"""

	dx = np.ones(len(R0))   # Correction vector
	Rn = np.array(R0)       # Approximation of solution
	errs = []

	def skew(u):
		# Span a (3,1) vector into its corresponding (3,3) skew symmetric matrix.

		return np.array([[0, -u[2], u[1]], [u[2], 0, -u[0]], [-u[1], u[0], 0]])

	def calc_step(X, y, Rn):
		# Solve function dx = inv(J'J)J'z.

		# Calculate Jacobian matrix.
		p = np.dot(Rn, X)
		# For independent errors the whole Jacobian is just the stacked matrix of individual Jacobians.
		J = np.vstack([-skew(pi) for pi in p.T])

		# Calculate error.
		err = y - p
		# For independent errors the whole error is just the stacked vector of individual errors.
		z = np.hstack([col for col in (y - p).T])

		return np.dot(np.linalg.inv(np.dot(J.T, J)), np.dot(J.T, z))

	its = 0
	while (its < maxits) and (dx[np.abs(dx) > tol].size > 0):
		# Calculate correction vector.
		dx = calc_step(X, y, Rn)
		# Update R.
		Rn = Rn + np.dot(skew(dx), Rn) # Adjust R.
		U,__,VT = np.linalg.svd(Rn)    # Apply SVD decomposition.
		Rn = np.dot(U, VT)             # Obtain the nearest rotation matrix.

		assert (Rn.T - np.linalg.inv(Rn) < 1e-10).all()	# Check if orthogonal.
		assert np.linalg.det(Rn) - 1 < 1e-10            # Check if its det is 1.

		# Update iteration.
		its += 1

		err = calc_err(X, y, Rn)
		errs.append(err)
		# print('  Iter: {0}, Error: {1}'.format(its, calc_err(X, y, Rn)))

	return Rn, err, its, errs

if __name__ == '__main__':

	args = get_args()

	np.random.seed(args.random_seed)

	# Load data.
	data = io.loadmat('./data/xy.mat')
	X, y = data['x'], data['y']

	# Solve the equation directly.
	sol = np.dot(y, np.linalg.inv(X))
	err = calc_err(X, y, sol)
	print("  Solution      : \r\n{}".format(sol))
	print("  Error         : {}".format(err))
	print('-'*50)
	if args.save_result:
		io.savemat('./result/solution0.mat', {'R':sol})

	# Generate some initial guesses.
	starts = []
	for i in range(args.num_init):
		R = np.random.randn(9).reshape((3,3)).astype('float64')
		if args.orthogonal:
			U,__,VT = np.linalg.svd(R)
			R = np.dot(U, VT)
		starts.append(R)

	if not os.path.exists('./result/'):
		os.makedirs('./result/')

	# Perform Gauss-Newton algprithm on each initial guess.
	errs_all = []
	min_err = err
	best_sol = sol
	for i, R0 in enumerate(starts):
		print("Start {}:".format(i + 1))
		print("  Initial guess : \r\n{}".format(R0))
		sol, err, its, errs = solve(X, y, R0, maxits = args.maxits)
		print("  Iterations    : {}".format(its))
		print("  Solution      : \r\n{}".format(sol))
		print("  Error         : {}".format(err))
		print('-'*50)
		errs_all.append(errs)
		if err < min_err:
			min_err = err
			best_sol = sol

	print("  Best Solution : \r\n{}".format(best_sol))
	print("  Error         : {}".format(min_err))
	print('-'*50)
	if args.save_result:
		io.savemat('./result/solution_best.mat', {'R':best_sol})

	# Display
	fig = plt.figure(figsize = (19.2,10.8))
	plt.title('Error')
	for i in range(len(starts)):
		plt.plot(np.arange(len(errs_all[i])), errs_all[i], label = 'Guess '+ str(i + 1))
	if args.show_legend:
		plt.legend()

	# Save graph to file.
	plt.savefig('./result/error' + str(len(starts)) + '{}'.format(args.orthogonal) + '.svg', bbox_inches = 'tight', format = 'svg')
	if args.show_graph:
		plt.show()