from math import *
from numpy import *
from myarray import *
import parameters
kEps = 1e-7

## The bezier curve's coefficient matrix, P(t) = tbar*M*P
M = matrix('-1. 3. -3. 1.; 3. -6. 3. 0.; -3. 3. 0. 0.; 1. 0. 0. 0.')

def sample_cubic_bezier_curve_chain( Cset, num_samples = 100 ):
	'''
	Given a set of bezier curve chain control points
	and a positive integer representing the number of samples per curve in the chain,
	returns a list of pairs ( samples, ts ) as would be returned by sample_cubic_bezier_curve().

	if curves are open, add samples of the straight line that connect the begin and the end.
	'''
	all_pts = []
	all_ts = []
	all_dts = []

	for P in Cset:
		samples, ts, dts = sample_cubic_bezier_curve( P, num_samples )
		all_pts.append( samples )
		all_ts.append( ts )
		all_dts.append( dts )

	return all_pts, all_ts, all_dts

def sample_cubic_bezier_curve( P, num_samples = 100 ):
	'''
	a 4-by-k numpy.array P containing the positions of the control points as the rows,
	return two lists: sample points of the bezier curve denoted in P, and corresponding t values
	'''
	if num_samples is None:
		num_samples = max(int(length_of_cubic_bezier_curve(P) / 1), 2)

	P = asarray( P )
	result = []
	tbar = ones( (4,1) )
	ts = linspace( 0, 1, num_samples )
	for t in ts:

		tbar[0] = t**3
		tbar[1] = t**2
		tbar[2] = t

		point = dot( P.T, dot( M.T, tbar ) )
		result.append( asarray(point).squeeze().tolist() )

	dts = ones( num_samples-1 ) * (1./(num_samples-1) )

	return asarray( result ), asarray( ts ), asarray( dts )

def sample_straight_line( begin, end, num_samples = 100 ):

	begin = asarray(begin)
	end = asarray(end)
	result = []

	if num_samples is None:
		num_samples = max(int(mag(begin - end) / 1), 2)

	ts = linspace( 0, 1, num_samples )
	for t in ts:

		point = (end - begin)*t + begin
		result.append(asarray(point).squeeze())

	return asarray( result ), asarray( ts )

def length_of_cubic_bezier_curve( P, num_samples = 100 ):
	'''
	a 4-by-k numpy.array P containing the positions of the control points as the rows,
	return a list of sample points of the bezier curve denoted in P
	'''

	P = asarray( P )
	assert P.shape[0] == 4

	samples = []
	tbar = ones( 4 )
	for t in linspace( 0, 1, num_samples ):
		tbar[0] = t**3
		tbar[1] = t**2
		tbar[2] = t
		tbar = tbar.reshape( (4,1) )
		point = dot( P.T, dot( M.T, tbar ) )
		samples.append( asarray(point).reshape(-1) )

	samples = asarray(samples)
	lengths = [mag(samples[i]-samples[i+1]) for i in range(len(samples)-1)]

	return sum( lengths )

def make_control_points_chain( controls, close = True ):
	'''
	make every control points a group from a chain of control points. Discard the remainder control points if there is any extra.
	'''
	controls = asarray(controls)
	remain_num = len( controls ) % 3

	if remain_num == 2:
		print 'wrong num for controls: ', len(controls), ': ', controls, 'closed:', close
	elif remain_num == 1:
		if close and not array_equal( controls[0], controls[-1] ):
			print 'wrong num for closed controls: ', len(controls), ': ', controls
	else:
		if not close:
			debugger()
			print 'wrong num for open controls: ', len(controls), ': ', controls

	num_segment = len( controls )//3
	if close:
		controls = concatenate( (controls[:num_segment*3], controls[0].reshape(1,2) ), axis=0 )
	elif not close and remain_num == 0:
		num_segment -= 1

	controls = controls[ :3*num_segment+1 ]

	Cset = []
	for i in range( num_segment ):
		Cset.append(controls[i*3:i*3+4].tolist())

	Cset = asarray( Cset )

	return Cset

def make_constraints_from_control_points( control_group, close=True ):
	'''
	Make default constraints based on the following assumptions:
	- if upon loading they appear to be G1, they should stay G1
	- C1 is G1
	- 90 degrees defaults to fixed angle
	- pinning should never be done; user can add later or handles placed near control points have that effect
	'''
	control_group = asarray( control_group )
	num = len( control_group )
	constraints = [ ['C0', False] for i in range( num ) ]

	for i in range( num ):
		## find the first control point in the same segment that has a different value with the first and last ones
		dir1, dir2 = zeros( 2 ), zeros( 2 )
		for j in range( 2, 5 ):
			if not array_equal( control_group[i, -1], control_group[i,-j] ):
				dir1 = dir( control_group[i,-1] - control_group[i,-j] )
				break

		for j in range( 1, 4 ):
			if not array_equal( control_group[(i+1)%num,j], control_group[(i+1)%num,0 ] ):
				dir2 = dir( control_group[(i+1)%num,j] - control_group[(i+1)%num,0 ] )
				break

# 		if i == 5 or i ==10:
# 			debugger()

		if allclose( dot( dir1, dir2 ), 1.0, atol=1e-02 ) and mag(dir1) != 0 and mag(dir2) != 0:
			if parameters.kG1andAconstraints:
				## G1
				constraints[ (i+1)%num ][0] = 'G1'
			else:
				## C1
				constraints[ (i+1)%num ][0] = 'C1'
		elif allclose( dot( dir1, dir2 ), 0, atol=1e-03 ) and mag(dir1) != 0 and mag(dir2) != 0:
			if parameters.kG1andAconstraints:
				## fixed angle
				constraints[ (i+1)%num ][0] = 'A'
			else:
				## C0
				constraints[ (i+1)%num ][0] = 'C0'

	if not close:
		constraints[0][0] = 'None'
		constraints.append( ['None', False] )

	return constraints

def split_cublic_beizer_curve( controls, partition ):
	'''
	controls are the four control points of an cubic bezier curve
	partition is an array has each splitted curve's portion
	e.g. [0.5, 0.3, 0.2] is an partition
	'''
	controls = asarray( controls )
	partition = asarray( partition )

	assert len(controls.shape) == 2
	assert controls.shape[0] == 4
	assert sum( partition ) == 1
	assert partition.any() > 0

	# num = len( partition )

	ref = 1.0
	for i in range( len(partition) ):
		partition[i] = partition[i] / ref
		ref = ( 1 - partition[i] ) * ref

	result = []
	for i, k in enumerate( partition[:-1] ):

		r1 = controls[:-1]*(1.-k) + controls[1:]*k
		r2 = r1[:-1]*(1.-k) + r1[1:]*k
		r3 = r2[:-1]*(1.-k) + r2[1:]*k

		result.append([controls[0].tolist(), r1[0].tolist(), r2[0].tolist(), r3[0].tolist()])

		controls = array( [r3[-1], r2[-1], r1[-1], controls[-1]] )

	result.append(controls)

	return asarray(result)


import sys
import math
import numpy as np
from random import seed, shuffle
from scipy.stats import multivariate_normal # generating synthetic data

def generate_synthetic_data(data_type, n_samples):

    """
        Code for generating the synthetic data.
        We will have two non-sensitive features and one sensitive feature.
        A sensitive feature value of 0.0 means the example is considered to be in protected group (e.g., female) and 1.0 means it's in non-protected group (e.g., male).
    """

    # in the same program run, we might generate this data multiple times (once for single and once for two models)
    # so make sure that the seed is always the same
    SEED = 1234
    seed(SEED)
    np.random.seed(SEED)

    def gen_gaussian(mean_in, cov_in, class_label):
        nv = multivariate_normal(mean = mean_in, cov = cov_in)
        X = nv.rvs(n_samples)
        y = np.ones(n_samples, dtype=float) * class_label
        return nv,X,y

    if data_type == 1:

        disc_factor = math.pi / 8.0 # this variable determines the initial discrimination in the data -- decraese it to generate more discrimination

        """ Generate the non-sensitive features randomly """
        # We will generate one gaussian cluster for each class
        mu1, sigma1 = [2, 2], [[5, 1], [1, 5]]
        mu2, sigma2 = [-2,-2], [[10, 1], [1, 3]]
        nv1, X1, y1 = gen_gaussian(mu1, sigma1, 1) # positive class
        nv2, X2, y2 = gen_gaussian(mu2, sigma2, -1) # negative class

        # join the posisitve and negative class clusters
        X = np.vstack((X1, X2))
        y = np.hstack((y1, y2))

        # shuffle the data
        perm = range(0,n_samples*2)
        shuffle(perm)
        X = X[perm]
        y = y[perm]

        rotation_mult = np.array([[math.cos(disc_factor), -math.sin(disc_factor)], [math.sin(disc_factor), math.cos(disc_factor)]])
        X_aux = np.dot(X, rotation_mult)

        """ Generate the sensitive feature here """
        x_control = [] # this array holds the sensitive feature value
        for i in range (0, len(X)):
            x = X_aux[i]

            # probability for each cluster that the point belongs to it
            p1 = nv1.pdf(x)
            p2 = nv2.pdf(x)

            # normalize the probabilities from 0 to 1
            s = p1+p2
            p1 = p1/s
            p2 = p2/s

            r = np.random.uniform() # generate a random number from 0 to 1

            if r < p1: # the first cluster is the positive class
                x_control.append(1.0) # 1.0 means its male
            else:
                x_control.append(0.0) # 0.0 -> female

        x_control = np.array(x_control)

    elif data_type == 2:

        ## More datasets here.
        pass

    return X,y,x_control


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

@author: Paolo
"""

## IMPORT APPROPRIATE MODULES
import numpy as np
from scipy.stats import gamma
import time

## 2) CODING AND APPLIED STATISTICS ##
### Drawn random samples from a given distribution

## Uniform random variables
def Draw_samples_from_Uni(lower_limit, upper_limit, size):
    ## constants based on ANSI C implementation (Saucier, 2000)
    mod = 2**32
    multiplier = 1103515245
    increment = 12345
    U = np.zeros(size) ## setup initial numpy vector to reserve memory space
    seed = round(time.time()) ## set seed according to current time
    for i in range(size):
        seed = (multiplier*seed + increment)%mod
        U[i] = lower_limit + (upper_limit - lower_limit) * seed/mod
    return U

## Draw samples from given distribution
def Draw_samples_from_dist(num_samples, dist, **params):
    uniform_samps = Draw_samples_from_Uni(0, 1, num_samples)

    ## Using inverse transform sampling and the Box-Muller transform
    if dist == "Normal":

        if "mean" in params:
            mean = params["mean"]
        else:
            print("Please specify 'mean' parameter.")
            return
        if "std" in params:
            std = params["std"]
        else:
            print("Please specify 'std' parameter.")
            return

        uniform_samps_pairs = Draw_samples_from_Uni(0, 1, num_samples)
        z = np.zeros(num_samples)
        for i in range(num_samples):
            z0 = np.sqrt(-2*np.log(uniform_samps[i]))*np.cos(2*np.pi*uniform_samps_pairs[i])
            # z1 = np.sqrt(-2*np.log(uniform_samps[i]))*np.sin(2*np.pi*uniform_samps_pairs[i]) ## however we can just use z0, we don't need pairs
            z[i] = z0*std + mean
        return z

    elif dist == "Exponential":

        if "lamb" in params:
            lamb = params["lamb"]
        else:
            print("Please specify 'lamb' parameter.")
            return

        x = np.zeros(num_samples)
        for i in range(num_samples):
            x[i] = -(1/lamb) * (np.log(1 - uniform_samps[i])) ## take the inverse Exponential CDF and apply inverse transform sampling
        return x

    elif dist == "Gamma":

        if "shape" in params:
            shape = params["shape"]
        else:
            print("Please specify 'shape' parameter.")
            return
        if "loc" in params:
            loc = params["loc"]
        else:
            print("Please specify 'loc' parameter.")
            return
        if "scale" in params:
            scale = params["scale"]
        else:
            print("Please specify 'scale' parameter.")
            return

        g = np.zeros(num_samples)
        for i in range(num_samples):
            g[i] = gamma.ppf(uniform_samps[i], shape, loc, scale)
        return g
    else:
        print("Please input a correct distribution. Type either 'Normal', 'Exponential' or 'Gamma'")

norm_samps = Draw_samples_from_dist(100, "Normal", mean = 0, std = 1)
expon_samps = Draw_samples_from_dist(100, "Exponential", lamb = 1)
gamma_samps = Draw_samples_from_dist(100, "Gamma", shape = 1, loc = 1, scale = 1)


import numpy as np
from mdsim.force.md_force import MDForce

class LJForce(MDForce):
    """
    LJForce class implements the Lennard-Jones force

    Reference
    ---------
    The LJ force takes the formula:
        Fij = (-12 x sigma^12 / rij^13 + 6 x sigma^6 / rij^7) * 4 * epsilon * [rij]/rij
    """
    def __init__(self):
        super().__init__()
        # set default sigma and epsilon parameter
        self._params = {'sigma': 1.0, 'epsilon': 1.0}

    def compute_force(self, coords):
        """
        Compute the Lennard-Jones force vector on current geometry

        Parameters
        ----------
        coords: numpy.ndarray of shape (Natoms, 3)
            Numpy array of atomic coordinates

        Returns
        -------
        force: numpy.ndarray of shape (Natoms, 3)
            Numpy array of gradients on each atom
        """
        sigma = self._params['sigma']
        epsilon = self._params['epsilon']
        # compute the distance between each atom pairs
        c_diff = coords[:,np.newaxis,:] - coords[np.newaxis,:,:]
        r2_mat = np.sum(np.square(c_diff), axis=-1)
        # prepare values for the LJ force formula
        s6 = sigma**6
        r2_mat2 = np.square(r2_mat)
        np.fill_diagonal(r2_mat2, 1.0) # prevent 1/0 error in equation
        r2_matn4 = 1.0 / np.square(r2_mat2)
        r2_matn7 = np.square(r2_matn4) * r2_mat
        # compute the magnitude of the gradients
        f_lj_mat = (-12.0*r2_matn7 * s6 + 6.0*r2_matn4) * 4 * epsilon * s6
        # contract with coordinates dR to get gradient vectors
        return np.einsum('ijk,ij->ik', c_diff, f_lj_mat)

    def compute_force_ref(self, coords):
        """
        Compute the Lennard-Jones force vector on current geometry

        Parameters
        ----------
        coords: numpy.ndarray of shape (Natoms, 3)
            Numpy array of atomic coordinates

        Returns
        -------
        force: numpy.ndarray of shape (Natoms, 3)
            Numpy array of gradients on each atom
        """

        noa = len(coords)
        s6 = self._params['sigma']**6
        epsilon = self._params['epsilon']
        forces = np.zeros((noa,3), dtype=float)
        for i in range(noa):
            for j in range(i+1,noa):
                dc = coords[i] - coords[j]
                r2 = dc[0]*dc[0] + dc[1]*dc[1] + dc[2]*dc[2]
                f = (-12 / r2**7 * s6 + 6 / r2**4) * 4 * epsilon * s6
                forces[i] += f * dc
                forces[j] -= f * dc
        return forces


import numpy as np

from opytimizer.math import hyper

def test_norm():
    array = np.array([[1, 1]])

    norm_array = hyper.norm(array)

    assert norm_array > 0

def test_span():
    array = np.array([[0.5, 0.75, 0.5, 0.9]])

    lb = [0]

    ub = [10]

    span_array = hyper.span(array, lb, ub)

    assert span_array > 0

def test_span_to_hyper_value():
    lb = np.full(1, 10)
    ub = np.full(1, 20)

    @hyper.span_to_hyper_value(lb, ub)
    def call(x):
        return np.sum(x)

    y = call(np.array([[0.5], [0.5]]))

    assert y == 30


"""
Implements test functions from
https://en.wikipedia.org/wiki/Test_functions_for_optimization

Plots the average func(x)'s each iteration.

Run main() to run the tests.
"""
from optimizer import *
from typing import Any
import numpy as np
import matplotlib.pyplot as plt

def plot_objective(title: str, *args: Any, log_scale: bool = False, show: bool = False, learning_rate: float = 3e-1, **kwargs: Any):
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    plt.title(title)
    plt.xlabel("iteration")
    plt.ylabel("y")
    (*_, x), avg_f, avg_f_slow = zip(*iter_optimize(*args, learning_rate=learning_rate, return_var=["x", "avg_func(x)", "avg_f_slow"], **kwargs))
    print(title, "Solution:", x)
    print(title, "Objective:", avg_f[-1])
    ax.plot(avg_f)
    ax.plot(avg_f_slow)
    if log_scale:
        ax.set_yscale("log")
    if show:
        plt.show()

test_cases = {}

def rastrigin(x):
    return np.mean(x**2 + 10 * (1 - np.cos(2*np.pi*x)))

test_cases["Rastrigin"] = dict(
    func=rastrigin,
    x=random_array(-5.12, 5.12, 25),
)

def ackley(x):
    return (
        -20 * np.exp(-0.2 * np.sqrt(0.5) * np.linalg.norm(x))
        - np.exp(np.mean(np.cos(2*np.pi*x)))
        + np.e
        + 20
    )

test_cases["Ackley"] = dict(
    func=ackley,
    x=random_array(-5, 5, 2),
    learning_rate=1e-1,
)

def sphere(x):
    return np.linalg.norm(x) ** 2

test_cases["Sphere"] = dict(
    func=sphere,
    x=random_array(-5, 5, 25),
    log_scale=True,
)

def rosenbrock(x):
    return np.mean(100*(x[1:] - x[:-1]**2)**2 + (1-x[:-1])**2)

test_cases["Rosenbrock"] = dict(
    func=rosenbrock,
    x=random_array(-2, 2, 25),
    learning_rate=3e-1,
    log_scale=True,
)

def beale(x):
    x, y = x
    return (
        (1.5 - x + x * y) ** 2
        + (2.25 - x + x * y**2) ** 2
        + (2.625 - x + x * y**3) ** 2
    )

test_cases["Beale"] = dict(
    func=beale,
    x=random_array(-4.5, 4.5, 2),
    log_scale=True,
)

def goldstein_price(x):
    x, y = x
    return (
        (1 + (x+y+1)**2 * (19 - 14*x + 3*x**2 - 14*y + 6*x*y + 3*y**2))
        * (30 + (2*x-3*y)**2 * (18 - 32*x + 12*x**2 + 48*y - 36*x*y + 27*y**2))
    )

test_cases["Goldstein-Price"] = dict(
    func=goldstein_price,
    x=random_array(-2, 2, 2),
    log_scale=True,
)

def booth(x):
    x, y = x
    return (x + 2*y - 7) ** 2 + (2*x + y - 5) ** 2

test_cases["Booth"] = dict(
    func=booth,
    x=random_array(-2, 2, 2),
    log_scale=True,
)

def bukin_n6(x):
    x, y = x
    return 100 * np.sqrt(abs(y - 0.01*x**2)) + 0.01*abs(x+10)

test_cases["Bukin function N.6"] = dict(
    func=bukin_n6,
    x=random_array(-2, 2, 2),
)

def matyas(x):
    x, y = x
    return 0.26 * (x**2 + y**2) - 0.48*x*y

test_cases["Matyas"] = dict(
    func=matyas,
    x=random_array(-10, 10, 2),
    perturbation=1e-2,
    log_scale=True,
)

def levi_n13(x):
    x, y = x
    return (
        np.sin(3*np.pi*x) ** 2
        + (x-1) ** 2 * (1 + np.sin(3*np.pi*y) ** 2)
        + (y-1) ** 2 * (1 + np.sin(2*np.pi*y) ** 2)
    )

test_cases["Levi function N.13"] = dict(
    func=levi_n13,
    x=random_array(-10, 10, 2),
)

def himmelblau(x):
    x, y = x
    return (x**2 + y - 11) ** 2 + (x + y**2 - 7) ** 2

test_cases["Himmelblau"] = dict(
    func=himmelblau,
    x=random_array(-5, 5, 2),
    log_scale=True,
)

def three_hump_camel(x):
    x, y = x
    return 2*x**2 - 1.05*x**4 + x**6/6 + x*y + y**2

test_cases["3-Hump Camel"] = dict(
    func=three_hump_camel,
    x=random_array(-5, 5, 2),
    log_scale=True,
)

def easom(x):
    return -np.prod(np.cos(x)) * np.exp(-np.linalg.norm(x-np.pi)**2)

test_cases["Easom"] = dict(
    func=easom,
    x=random_array(-10, 10, 2),
    learning_rate=1e-2,
    perturbation=1e-2,
    abs_tol_x=1e-3,
    abs_tol_f=1e-3,
)

def cross_in_tray(x):
    return -1e-4 * (-holder_table(x) + 1) ** 0.1

test_cases["Cross-in-Tray"] = dict(
    func=cross_in_tray,
    x=random_array(-10, 10, 2),
    abs_tol_x=1e-6,
    abs_tol_f=1e-6,
)

def eggholder(x):
    x, y = x
    return -y * np.sin(np.sqrt(abs(x/2 + y))) - x * np.sin(np.sqrt(abs(x - y)))

test_cases["Eggholder"] = dict(
    func=eggholder,
    x=random_array(-512, 512, 2),
)

def holder_table(x):
    return -abs(np.prod(np.sin(x))) * np.exp(abs(1 - np.linalg.norm(x) / np.pi))

test_cases["Holder Table"] = dict(
    func=holder_table,
    x=random_array(-10, 10, 2),
)

def mc_cormick(x):
    x, y = x
    return np.sin(x+y) + (x-y)**2 - 1.5*x + 2.5*y + 1

test_cases["McCormick"] = dict(
    func=mc_cormick,
    x=random_array(-1.5, 4, 2),
)

def schaffer_n2(x):
    x, y = x
    return 0.5 + (np.sin(x**2 - y**2) ** 2 - 0.5) / (1 + 0.001*(x**2 + y**2)) ** 2

test_cases["Schaffer function N.2"] = dict(
    func=schaffer_n2,
    x=random_array(-100, 100, 2),
    learning_rate=1e-3,
    perturbation=3e-3,
)

def schaffer_n4(x):
    x, y = x
    return 0.5 + (np.cos(np.sin(x**2 - y**2)) ** 2 - 0.5) / (1 + 0.001*(x**2 + y**2)) ** 2

test_cases["Schaffer function N.4"] = dict(
    func=schaffer_n4,
    x=random_array(-100, 100, 2),
    learning_rate=1e-2,
    perturbation=3e-3,
)

def styblinski_tang(x):
    return np.mean(x**4 - 16*x**2 + 5*x)

test_cases["Styblinski-Tang"] = dict(
    func=styblinski_tang,
    x=random_array(-5, 5, 25),
)

def main():
    for title, settings in test_cases.items():
        plot_objective(title, **settings)
    plt.show()

if __name__ == "__main__":
    main()


import numpy as np

# input fungsi ODE yang diinginkan
def dydx(x, y):
    # return (1 + y**2)
    return((0.7*y*(1-(y/750)))-20)

def rungekutta(x0, y0, x, h):
    # step height h
    n = np.int((x - x0) / h)
    # iterasi
    y = y0
    for i in range(1, n + 1):

        # formulasi runge-kutta 4th order
        k1 = h * dydx(x0, y)
        k2 = h * dydx(x0 + 0.5 * h, y + 0.5 * k1)
        k3 = h * dydx(x0 + 0.5 * h, y + 0.5 * k2)
        k4 = h * dydx(x0 + h, y + k3)
        y = y + (1.0 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4)

        x0 = x0 + h
    return y

# input nilai yang diinginkan
x0 = 0
y0 = 30
x = 100
h = 0.1
hasil = rungekutta(x0, y0, x, h)
print(hasil)


"""
=======================================================
Comparison of LDA and PCA 2D projection of Siri dataset
=======================================================

The Siri dataset represents 3 kind of Siri classes (Type 1, Type 2, Type 3)
with 4 attributes: attr_SL, attr_SQ, attr_PL, attr_PW

Principal Component Analysis (PCA) applied to this data identifies the
combination of attributes (principal components, or directions in the
feature space) that account for the most variance in the data. Here we
plot the different samples on the 2 first principal components.

Linear Discriminant Analysis (LDA) tries to identify attributes that
account for the most variance *between classes*. In particular,
LDA, in contrast to PCA, is a supervised method, using known class labels.
"""

print(__doc__)

import matplotlib.pyplot as plt
import numpy as np

from sklearn import datasets
from sklearn.decomposition import PCA
from sklearn.lda import LDA

# read in csv dataset
siri = np.loadtxt('Siri_data_with_class.csv', delimiter=',',skiprows=1, dtype={'names':('Attr_SL','Attr_SQ','Attr_PL','Attr_PW','class'),'formats':('f2','f2','f2','f2','S6')})
X = np.array([list(i)[:4] for i in siri])
y = np.array([int(list(i)[4][5:]) for i in siri])
target_names = np.array(['Type 1','Type 2','Type 3'])

# choose the top 2 best attributes by PCA algo
pca = PCA(n_components=2)
X_r = pca.fit(X).transform(X)

#lda is a supervised method, use target y
## choose the top 2 best attributes by LDA algo
lda = LDA(n_components=2)
X_r2 = lda.fit(X, y).transform(X)

# Percentage of variance explained for each components
print('explained variance ratio (first two components): %s'
      % str(pca.explained_variance_ratio_))

# draw scatterplot
plt.figure()
for c, i, target_name in zip("rgb", [1, 2, 3], target_names):
    plt.scatter(X_r[y == i, 0], X_r[y == i, 1], c=c, label=target_name)
plt.legend()
plt.title('PCA of SIRI dataset')

plt.figure()
for c, i, target_name in zip("rgb", [1, 2, 3], target_names):
    plt.scatter(X_r2[y == i, 0], X_r2[y == i, 1], c=c, label=target_name)
plt.legend()
plt.title('LDA of SIRI dataset')

plt.show()

dimension reduction/dimension reduction_PCA & LDA_siri.py


import matplotlib.pyplot as plt
import numpy as np
from distributions import Binomial_distribution, Discrete_uniform_distribution

def plotDistributeBar(ax, data, label='', width=0.3, offset=0, title='Probability Distribution of true'):
    ax.bar(np.arange(len(data))+offset,data,width=width,label=label)

    fontSize = 12
    ax.set_title(title,fontsize=fontSize)

    plt.xlabel('Different Teeth Bins',fontsize=fontSize)
    plt.ylabel('Probability',fontsize=fontSize)
    plt.xticks(np.arange(len(data)))

def plorDataDis(true_data,uniform,bino):
    ax = plt.subplot(1,1,1)
    offset = 0
    width=0.2
    plotDistributeBar(ax, true_data, label='True data', offset=offset, width=width)

    offset += width
    plotDistributeBar(ax, uniform, label='Uniform data', offset=offset, width=width)

    offset += width
    print(bino)
    plotDistributeBar(ax, bino, label='Binomial data', offset=offset, width=width)
    plt.legend()
    plt.show()

def get_klpq_div(p_probs, q_probs):
    kl_div = 0.0
    for pi, qi in zip(p_probs, q_probs):
        kl_div += pi*np.log(pi/qi)

    return kl_div

def get_klqp_div(p_probs, q_probs):
    kl_div = 0.0
    for pi, qi in zip(p_probs, q_probs):
        kl_div += qi*np.log(qi/pi)

    return kl_div

def plotKLResult(true_data,minP):
    ax = plt.subplot(1,1,1)
    offset = 0
    width=0.2
    plotDistributeBar(ax, true_data, label='True data', offset=offset, width=width)

    pAll = [0.02,0.1,minP,0.8]
    for p in pAll:
        offset += width
        dis = Binomial_distribution(N=len(true_data),p=p)
        plotDistributeBar(ax, dis, label='Binomial '+ str(p), offset=offset, width=width)

    plt.legend()
    plt.show()

def testDiscretKL():
    true_data = [0.02, 0.03, 0.15, 0.14, 0.13, 0.12, 0.09, 0.08, 0.1, 0.08, 0.06]
    print('sum=', sum(true_data))
    assert sum(true_data)==1.0
    unif_data = Discrete_uniform_distribution(true_data,N=len(true_data))
    bino_data = Binomial_distribution(N=len(true_data),p=0.3)

    plorDataDis(true_data,unif_data,bino_data)

    print('KL(True||Uniform): ', get_klpq_div(true_data,unif_data))
    print('KL(True||Binomial): ', get_klpq_div(true_data,bino_data))

    p = np.arange(0.02, 1.0, 0.02) #np.linspace(0, 1.0, 50)
    klpq = [get_klpq_div(true_data,Binomial_distribution(N=len(true_data),p=i)) for i in p]
    klqp = [get_klqp_div(true_data,Binomial_distribution(N=len(true_data),p=i)) for i in p]

    print('minimal klpq,', np.argmin(klpq), np.min(klpq))

    ax = plt.subplot(1,1,1)
    plotDistribute(ax,p,klpq,label='KL(P||Q)')
    plotDistribute(ax,p,klqp,label='KL(Q||P)')
    plotDistribute(ax,p,np.array(klpq)-np.array(klqp),label='KL(P||Q)-KL(Q||P)')
    plt.show()

    plotKLResult(true_data, np.min(klpq))

def plotDistribute(ax,x,y,label='', title='Binomial P vs KL'):
    ax.plot(x,y,label=label)

    fontSize = 12
    ax.set_title(title,fontsize=fontSize)
    ax.legend()
    plt.xlabel('Binomial P',fontsize=fontSize)
    plt.ylabel('KL(P||Q) divergence',fontsize=fontSize)
    #plt.show()

def main():
    testDiscretKL()

if __name__=='__main__':
    main()


#!/usr/bin/env python3
"""
Bayesian Probability
"""
import numpy as np

def marginal(x, n, P, Pr):
    """
    Calculates the marginal probability of obtaining the data:

    x is the number of patients that develop severe side effects
    n is the total number of patients observed
    P is a 1D numpy.ndarray containing the various hypothetical probabilities
    of patients developing severe side effects
    Pr is a 1D numpy.ndarray containing the prior beliefs about P
    If n is not a positive integer, raise a ValueError with the message n must
    be a positive integer
    If x is not an integer that is greater than or equal to 0, raise a
    ValueError with the message x must be an integer that is greater than or
    equal to 0
    If x is greater than n, raise a ValueError with the message x cannot be
    greater than n
    If P is not a 1D numpy.ndarray, raise a TypeError with the message P must
    be a 1D numpy.ndarray
    If Pr is not a numpy.ndarray with the same shape as P, raise a TypeError
    with the message Pr must be a numpy.ndarray with the same shape as P
    If any value in P or Pr is not in the range [0, 1], raise a ValueError
    with the message All values in {P} must be in the range [0, 1] where {P}
    is the incorrect variable
    If Pr does not sum to 1, raise a ValueError with the message Pr must sum
    to 1
    All exceptions should be raised in the above order

    Returns: the marginal probability of obtaining x and n
    """
    if not isinstance(n, int) or n < 1:
        raise ValueError("n must be a positive integer")
    if not isinstance(x, (int, float)) or (x < 0):
        mg = "x must be an integer that is greater than or equal to 0"
        raise ValueError(mg)
    if x > n:
        raise ValueError("x cannot be greater than n")
    if not isinstance(P, np.ndarray) or len(P.shape) != 1:
        raise TypeError("P must be a 1D numpy.ndarray")
    if not isinstance(Pr, np.ndarray) or P.shape != Pr.shape:
        raise TypeError("Pr must be a numpy.ndarray with the same shape as P")
    if np.any(P > 1) or np.any(P < 0):
        raise ValueError("All values in P must be in the range [0, 1]")
    if np.any(Pr > 1) or np.any(Pr < 0):
        raise ValueError("All values in Pr must be in the range [0, 1]")
    if not np.isclose([np.sum(Pr)], [1])[0]:
        raise ValueError("Pr must sum to 1")

    num = (np.math.factorial(n))
    den = (np.math.factorial(x) * np.math.factorial(n - x))
    fct = num / den
    D = fct * (P**x) * ((1 - P)**(n - x))
    its = D * Pr

    return np.sum(its)


# Basic functions on polynomials

import numpy as np
import matplotlib.pyplot as plt

# Polynomials as lists of coefficients

# P+Q: max(deg(P),deg(Q)) operations

def add_polynomials(P, Q):
    somme = []
    n = len(P)
    m = len(Q)
    if n < m:
        P += (m - n) * [0]
    elif n < m:
        Q += (n - m) * [0]
    deg_max = max(n, m)
    for k in range(0, deg_max):
        somme += [P[k] + Q[k]]
    return somme

# aP: deg(P) operations
def multiply_polynomial(P, a):
    n = len(P)
    for k in range(0, n):
        P[k] *= a
    return P

# PQ: (deg(P)+deg(Q))**2 operations
def multiply_polynomials(P, Q):
    n = len(P)
    m = len(Q)
    P += m * [0]
    Q += n * [0]
    p = n + m
    # thus deg(PQ)=n+m
    res = []
    for k in range(0, p - 1):
        a_k = 0
        for i in range(0, p - 1):
            a_k += P[i] * Q[k - i]
        res += [a_k]
    return res

# P(x): deg(P) operations
def polynomial_image(P, x):
    n = len(P)
    sigma = 0
    for k in range(0, n):
        sigma += P[k] * x ** k
    return sigma

# int(P): 3deg(P) operations
def polynomial_integral(P, a, b):
    n = len(P)
    Q = []
    for k in range(0, n):
        Q += [P[k] / (k + 1)]
    Q = [0] + Q
    I = polynomial_image(Q, b) - polynomial_image(Q, a)
    return I

# P: deg(P) operations
def polynomial_derivative(P):
    n = len(P)
    dP = []
    for k in range(1, n):
        dP += [P[k] * (k)]
    return dP

def polynomial_graph(P, a, b):
    coordsx = np.linspace(a, b, 1000)
    coordsy = [polynomial_image(P, x) for x in coordsx]
    plt.clf()
    plt.plot(coordsx, coordsy, "r-", linewidth=2)

# Two useful utils to compute power series : factorial and binomial coeffs

# log(n) operations
def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

# 3*log(n) operations
def binomial_coefficients(k, n):
    u = factorial(n) / (factorial(k) * factorial(n - k))
    return u


# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf

tf.reset_default_graph()

x_data = np.linspace(0.0, 10.0, 1000000)
y_data = (1.7*x_data) + 3

batch_size = 8

w = tf.Variable(0.5)
b = tf.Variable(0.5)

xph = tf.placeholder(tf.float32, [batch_size])
yph = tf.placeholder(tf.float32, [batch_size])

y_model = w*xph + b

error = tf.reduce_sum(tf.square(yph-y_model))
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.001)
train = optimizer.minimize(error)

init = tf.global_variables_initializer()

saver = tf.train.Saver()

with tf.Session() as sess:
  sess.run(init)
  batches = 10000
  for i in range(batches):
    rand_ind = np.random.randint(len(x_data),size=batch_size)
    print(rand_ind)
    feed = {xph:x_data[rand_ind], yph:y_data[rand_ind]}
    sess.run(train, feed_dict=feed)
  model_w, model_b = sess.run([w,b])
  saver.save(sess,'models/6_model.ckpt')

x_test = np.linspace(-1,11,10)
y_pred_plot = model_w*x_test + model_b

plt.plot(x_test,y_pred_plot,'r')
plt.plot(x_data,y_data,'*')
print("slope: ", model_w, "intercept", model_b)

#with tf.Session() as sess:
#    # Restore the model
#    saver.restore(sess,'models/my_first_model.ckpt')
#    # Fetch Back Results
#    restored_w , restored_b = sess.run([w,b])
#
#x_test = np.linspace(-1,11,10)
#y_pred_plot = restored_w*x_test + restored_b
#
#plt.plot(x_test,y_pred_plot,'r')
#plt.plot(x_data,y_data,'*')


import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize

def func(X):
    F = (X[0]+X[1])**2 + (X[1]+X[2])**2
    return F
def f_con(X):
    G = X[0] + 2.0*X[1] + 3.0*X[2] - 1.0
    return G
x0 = np.array([-4.0, 1.0, 2.0])
res = optimize.fmin_slsqp(func, x0, f_eqcons=f_con, iter=1000, acc=1e-06, disp=True, full_output=True)

# conz = ({'type': 'eq', 'fun': f_con})
# res2 = optimize.minimize(func, x0, method='L-BFGS-B', constraints=conz, options={'disp': True})


import numpy as np

def retroactive_resolution(coefficients: np.matrix, vector: np.array) -> np.array:
    """
    This function performs a retroactive linear system resolution
        for triangular matrix
    Examples:
        2x1 + 2x2 - 1x3 = 5         2x1 + 2x2 = -1
        0x1 - 2x2 - 1x3 = -7        0x1 - 2x2 = -1
        0x1 + 0x2 + 5x3 = 15
    >>> gaussian_elimination([[2, 2, -1], [0, -2, -1], [0, 0, 5]], [[5], [-7], [15]])
    array([[2.],
           [2.],
           [3.]])
    >>> gaussian_elimination([[2, 2], [0, -2]], [[-1], [-1]])
    array([[-1. ],
           [ 0.5]])
    """

    rows, columns = np.shape(coefficients)

    x = np.zeros((rows, 1), dtype=float)
    for row in reversed(range(rows)):
        sum = 0
        for col in range(row + 1, columns):
            sum += coefficients[row, col] * x[col]

        x[row, 0] = (vector[row] - sum) / coefficients[row, row]

    return x

def gaussian_elimination(coefficients: np.matrix, vector: np.array) -> np.array:
    """
    This function performs Gaussian elimination method
    Examples:
        1x1 - 4x2 - 2x3 = -2        1x1 + 2x2 = 5
        5x1 + 2x2 - 2x3 = -3        5x1 + 2x2 = 5
        1x1 - 1x2 + 0x3 = 4
    >>> gaussian_elimination([[1, -4, -2], [5, 2, -2], [1, -1, 0]], [[-2], [-3], [4]])
    array([[ 2.3 ],
           [-1.7 ],
           [ 5.55]])
    >>> gaussian_elimination([[1, 2], [5, 2]], [[5], [5]])
    array([[0. ],
           [2.5]])
    """
    # coefficients must to be a square matrix so we need to check first
    rows, columns = np.shape(coefficients)
    if rows != columns:
        return []

    # augmented matrix
    augmented_mat = np.concatenate((coefficients, vector), axis=1)
    augmented_mat = augmented_mat.astype("float64")

    # scale the matrix leaving it triangular
    for row in range(rows - 1):
        pivot = augmented_mat[row, row]
        for col in range(row + 1, columns):
            factor = augmented_mat[col, row] / pivot
            augmented_mat[col, :] -= factor * augmented_mat[row, :]

    x = retroactive_resolution(
        augmented_mat[:, 0:columns], augmented_mat[:, columns : columns + 1]
    )

    return x

if __name__ == "__main__":
    import doctest

    doctest.testmod()


import matplotlib.pyplot as plt
from scipy import *
from scipy import integrate
from scipy.integrate import ode, odeint
import numpy as np

# Initial setup
xx0 = np.array([.25,1.5])
mu =0.1
xmin,xmax = -2,2
ymin,ymax = -2,2
plt.close()
fig,ax0 = plt.subplots(1,1,figsize=(8,8))
ax0.set_facecolor(plt.cm.gray(.95))
#ax.axis('equal')

#Vector field
x1,y1 = np.linspace(xmin,xmax,200),np.linspace(ymin,ymax,200)
X,Y = np.meshgrid(x1,y1 )
U = mu*X - Y +X*Y**2
V = X + mu*Y + Y**3
start = [[0.3,0],[.6,.5]]

#Plotting
strm = ax0.streamplot( X,Y,U, V,linewidth=.2)
try:
    strmS = ax0.streamplot(x1,y1, U, V, start_points=start, color="crimson", linewidth=.5)
except: # IndexError:
    print('Unable to plot initial value trajectories')
fig.suptitle('Hopf Bifurcation', size=16)
ax0.set_title( 'Strogatz 8.2.1, p.256', size=12)
ax0.text(xmin+.25,ymax-.25, r'$\mu = {0}$'.format(mu),size=14)
ax0.plot(y1/(mu+y1**2),x1, 'k-.',lw=.8, label=r'x-nullcline: $x = \frac{y}{(\mu+y^2)}$')
ax0.plot(-mu*y1+y1**3,x1, 'k:', lw=.8, label=r'y-nullcline: $x =  -\mu y + y^3}$')
ax0.set_xlim([xmin,xmax])
ax0.set_ylim([ymin,ymax])
ax0.set_xlabel(r"$ x$",size=14)
ax0.set_ylabel(r"$ y$",size=14)
plt.legend()
plt.grid(True)
plt.legend()
plt.grid(True)
plt.show()


"""
Day 3 Solutions
"""

import itertools as it

import numpy as np

def part1(input_lines):
    """
    You come across an experimental new kind of memory stored on an infinite two-dimensional grid.

    Each square on the grid is allocated in a spiral pattern starting at a location marked 1
    and then counting up while spiraling outward. For example, the first few squares are
    allocated like this:

    17  16  15  14  13
    18   5   4   3  12
    19   6   1   2  11
    20   7   8   9  10
    21  22  23---> ...

    While this is very space-efficient (no squares are skipped), requested data must be
    carried back to square 1 (the location of the only access port for this memory system)
    by programs that can only move up, down, left, or right. They always take the shortest path:
    the Manhattan Distance between the location of the data and square 1.

    For example:

        - Data from square 1 is carried 0 steps, since it's at the access port.
        - Data from square 12 is carried 3 steps, such as: down, left, left.
        - Data from square 23 is carried only 2 steps: up twice.
        - Data from square 1024 must be carried 31 steps.

    How many steps are required to carry the data from the square identified in your puzzle
    input all the way to the access port?
    """
    # Note: Rotation is right -> top -> left -> bottom; starting one cell away from the corner.
    # The size of the array is the smallest square-of-an-odd-int that fits our count
    # 1, 9, 25, etc.
    cell = int(input_lines[0])
    # (Special base case for 1)
    if cell == 1:
        return 0
    diam = 1
    while diam ** 2 < cell:
        diam += 2
    radius = diam // 2
    inner_area = (diam - 2) ** 2
    # Consider our cell as being on the "perimeter". We basically need to determine
    # distance-to-center of the side we're on (corners being the farthest away)
    # For the 3x3 case, this goes like so as we walk the spiral: 0, 1, 0, 1, 0, 1, 0, 1
    # The 5x5 case goes: 1, 0, 1, 2, 1, 0, 1, 2, ...
    # The 7x7 case goes: 2, 1, 0, 1, 2, 3, ...
    # Note that it's (decreasing, 0, increasing), shifted left by 1 due to where we start the spiral.
    # We use a modulus of diameter - 1 to rotate through that array, finding that delta.
    mod = diam - 1
    deltas = abs(np.array(range(mod)) - radius + 1)
    delta_to_center_of_side = deltas[(cell - inner_area) % mod - 1]
    # Now our distance is that delta + the radius!
    return delta_to_center_of_side + radius

def part2(input_lines):
    """
    As a stress test on the system, the programs here clear the grid and then store the value 1 in square 1.
    Then, in the same allocation order as shown above, they store the sum of the values in all adjacent squares,
    including diagonals.

    So, the first few squares' values are chosen as follows:

        - Square 1 starts with the value 1.
        - Square 2 has only one adjacent filled square (with value 1), so it also stores 1.
        - Square 3 has both of the above squares as neighbors and stores the sum of their values, 2.
        - Square 4 has all three of the aforementioned squares as neighbors and stores the sum of their values, 4.
        - Square 5 only has the first and fourth squares as neighbors, so it gets the value 5.

    Once a square is written, its value does not change. Therefore, the first few squares would receive
    the following values:

    147  142  133  122   59
    304    5    4    2   57
    330   10    1    1   54
    351   11   23   25   26
    362  747  806--->   ...

    What is the first value written that is larger than your puzzle input?
    """
    target = int(input_lines[0])
    g = SpiralGrid()
    return g.locate_target_sum(target)

class SpiralGrid:
    """
    It was easier for me to conceptualize this as a class, so I did.

    This implements the spiral grid plus the debug storage method above.
    It knows how to run one layer of the spiral at a time, and how to
    reverse-search its data store for the right target value.
    """
    def __init__(self):
        self.x = 0
        self.y = 0
        self.cell = 2
        self.layer = 1
        self.map = {(0,0): 1}
        self.data = [1]

    def store(self):
        sum_ = 0
        for (dx, dy) in it.product((-1, 0, 1), repeats=2):
                neighbor = (self.x + dx, self.y + dy)
                if neighbor in self.map:
                    nval = self.data[self.map[neighbor] - 1]
                    sum_ += nval
        self.map[(self.x, self.y)] = self.cell
        self.data.append(sum_)
        self.cell += 1

    def up(self):
        self.y += 1
        self.store()

    def down(self):
        self.y -= 1
        self.store()

    def left(self):
        self.x -= 1
        self.store()

    def right(self):
        self.x += 1
        self.store()

    def fill_layer(self):
        side_len = (2 * self.layer)
        # Right to start
        self.right()
        # Up to the corner
        for _ in range(side_len - 1):
            self.up()
        # Left, then down, then right
        for _ in range(side_len):
            self.left()
        for _ in range(side_len):
            self.down()
        for _ in range(side_len):
            self.right()
        # Increment working layer
        self.layer += 1

    def get_cell_number(self, x, y):
        while self.layer <= max(abs(x), abs(y)):
            self.fill_layer()
        return self.map[(x, y)]

    def locate_target_sum(self, target):
        # We just do this one layer at a time,
        # then scan backwards once we're in the right layer.
        while self.data[-1] <= target:
            self.fill_layer()
        i = -1
        while self.data[i] > target:
            i -= 1
        return self.data[i + 1]


# Communities formation using Girvan-Newman algorithm

import networkx as nx

# Which edge to remove from the graph to form two communities ?
# This is done by calculating the betweenness centrality for each edge, and higher the betweenness centrality of an edge, sooner it is removed.
def edge_to_remove(G):
	dict1 = nx.edge_betweenness_centrality(G) # Returns dictionary, with 'edge' as key and 'edge betweenness centrality' as value
	# Now we have to sort edges according to their betweenness centrality measures.
	list_of_tuples = dict1.items()
	list_of_tuples.sort(key = lambda x: x[1], reverse = True)
	return list_of_tuples[0][0] # Returns an edge (a,b) as a tuple ----> ((a,b))

# Function to implement Girvan-Newman algorithm.
def girvan(G):
	c = list(nx.connected_component_subgraphs(G))
	l = len(c)
	print 'The number of connected components in the graph : ', l

	while (l==1):
		G.remove_edge(*edge_to_remove(G))  # ((a,b)) ----> (a,b). That's why we use *
		c = list(nx.connected_component_subgraphs(G))
		l = len(c)
		print 'The number of connected components in the graph : ', l

	return c

G = nx.karate_club_graph() # Creating Karate Club network using builtin function
c = girvan(G) # Detecting communities in the network
print 'The communities formed are as follows :\n'
for i in c :
	print i.nodes()
	print 'Size of the community : ', i.number_of_nodes()
	print '\n'


import numpy as np

def score_game(game_core, predict_func):
  '''Запускаем игру 1000 раз, чтобы узнать, как быстро игра угадывает число'''
  count_ls = []
  np.random.seed(1)  # фиксируем RANDOM SEED, чтобы ваш эксперимент был воспроизводим!
  random_array = np.random.randint(1,101, size=(1000))
  for number in random_array:
    count_ls.append(game_core(number, predict_func))
  score = int(np.mean(count_ls))
  print(f"Ваш алгоритм угадывает число в среднем за {score} попыток")
  return(score)

def game_core(number, predict_func):
  '''Угадываем число, сокращая диапазон для угадывания'''
  r_min = 1
  r_max = 100
  count = 1
  predict = np.random.randint(r_min, r_max + 1)

  while predict != number:
    count += 1
    if number > predict:
      r_min = predict + 1
      predict = predict_func(r_min, r_max)
    else:
      r_max = predict - 1
      predict = predict_func(r_min, r_max)

  return(count)

def gen_predict_v1(r_min, r_max):
  '''Угадываем число через случайную функцию'''
  return np.random.randint(r_min, r_max+1)

def gen_predict_v2(r_min, r_max):
  '''Угадываем число бинарным способом'''
  return (r_max + r_min) // 2

# Запускаем программу
print('Угадываем случайно:')
score_game(game_core, gen_predict_v1)

print()

print('Угадываем бинарно:')
score_game(game_core, gen_predict_v2)


#########################################################################
######  Name: Kenneth Emeka Odoh
######  Purpose: An implementation of Recursive Linear Regression
#########################################################################
from __future__ import division
import pandas as pd
import numpy as np

class OnlineLinearRegression:
    """
    This is based on the Recursive Linear regression classifier

    [1] Galanos, K. Recursive least squares. Retrieved 06/15, 2017, from http://saba.kntu.ac.ir/eecd/people/aliyari/NN%20%20files/rls.pdf

    """

    def __init__(self):
        '''
            accept the data in a matrix format
        '''
        self.error = None
        self.theta = None
        self.cov = None
        self.isTrained = False

    def update(self, x, y):
        '''
            accept in same format and dimension as data in constructor
        '''

        #set the prior
        self.priorY = y

        self.priorX = x

        xm = np.mat (x)
        ym = np.mat (y)

        all_zeros = not np.any(self.error)
        if all_zeros:
            self.error = np.zeros(y.shape)

        all_zeros = not np.any(self.theta)
        if all_zeros:
            #self.theta = ( (xm.T * xm ).I ) * xm.T *  ym

            tmat = np.linalg.pinv( np.nan_to_num(xm.T * xm) ) #ensure stability
            self.theta = ( tmat ) * xm.T *  ym
            self.isTrained = True

        all_zeros = not np.any(self.cov)
        if all_zeros:
            self.cov = np.dot ( x.T, x)

        if not self.isTrained:

            cov = np.mat ( self.cov )

            theta = np.mat ( xm * self.theta )

            self.error = ym - ( theta  )

            Im = np.mat ( np.eye (x.shape[1]) )

            self.cov = cov * np.mat( Im - ( ( xm.T * xm * cov ) / (1 + ( xm * cov * xm.T )  ) )  )

            self.theta = theta + ( self.cov * xm.T *  self.error  )

            self.isTrained = False

    def getA(self):
        """
            outputs the A vector
        """
        return self.theta

    def getB(self):
        """
            outputs the noise or bias
        """
        return self.error

    def getCovarianceMatrix(self):
        """
            outputs the covariance matrix
        """
        #ypost = np.dot ( self.getA().T, self.priorX )

        theta = np.mat ( self.getA() )
        Xm = np.mat ( self.priorX )

        ypost = Xm * theta
        yprior = self.priorY
        error = ypost - yprior
        #error = error - np.mean ( error, axis = 0 )
        return np.dot ( error.T, error )

    def getCovarianceNoiseMatrix(self):
        """
            outputs the noise covariance matrix, R
        """
        return np.dot ( self.getB().T, self.getB() )


import numpy as np
import scipy.spatial.distance as sp_dist

from scratch.abstract import AbstractModel

class KNearestNeighbors(AbstractModel):
    """
    Despite virtually all statistical / machine learning algorithms being
    available as easily callable functions from open source libraries, I
    believe a strong working knowledge of the algorithms is still imperative.
    It has been a hobby of mine to code these algorithms from scratch to
    confirm and expand my working knowledge. I am going to start sharing some
    of this work in a series of blog posts. This first post will be on k-nearest
    neighbors.

    K-nearest neighbors is an algorithm that can be used for both classification
    and regression, although it is probably most commonly used for classification.
    The flow of the algorithm is nearly identical for continuous and categorical
    dependent variables. The only difference is how the nearest neighbors are
    combined to produce the final prediction.

    The process of k-nearest neighbors is:

    1. Compute the distance between every observation in the training dataset and
    every observation in the prediction dataset. The distance metric used in my
    version below is the Minkowski distance of order p. Minkowski distance of order
    p is $D(X, Y) = \sqrt(\sum_{i=1}^{n}(x_{i} - y_{i})^{2})$ where each *i* is a
    different feature of the observation vector.
    2. Select k observations from the training dataset for each observation in the
    prediction dataset. The k hyperparameter is either set in advance by the user or
    optimized through some search procedure. In this case, the k value is set to 5.
    The k observations are those with the minimum distances.
    3. Aggregate the k observations to produce the final predicted value for the
    observation vector in the prediction dataset. In the case of this classification
    example, make the prediction the category value that occurs most often in the k
    observations of the training dataset. For a regression problem, the output value
    from the k observations might instead be the mean average.

    Below is the coded from scratch version of the k-nearest neighbors algorithm and
    the results of running the algorithm on the breast cancer dataset.
    """
    def __init__(self, n_neighbors):
        self.n_neighbors = n_neighbors

    def fit(self, X, y):
        self.Xtrain = X
        self.ytrain = y

    def predict(self, X):
        n = X.shape[0]
        dists = sp_dist.cdist(X, self.Xtrain, "minkowski", p=2)
        min_idx = [dists[i, :].argsort()[:self.n_neighbors] for i in range(n)]
        yhat = np.array([np.argmax(np.bincount(self.ytrain[i])) for i in min_idx])
        return yhat


import scipy

from .regularizer import Regularizer

class Boundedl2(Regularizer):
    """
    The bounded l2 regularization is equal to
        R(x) = sum_{i=1}^d x_i^2 / (x_i^2 + 1)
    where x=(x_1, ..., x_d) is from R^d. This penalty is attractive for benchmarking
    purposes since it is smooth (has Lipschitz gradient) and nonconvex.

    See
        https://arxiv.org/pdf/1905.05920.pdf
        https://arxiv.org/pdf/1810.10690.pdf
    for examples of using this penalty for benchmarking.
    """
    def __init__(self, coef):
        self.coef = coef

    def value(x, x2=None):
        if not scipy.sparse.issparse(x):
            if x2 is None:
                x2 = x * x
            return self.coef * 0.5 * np.sum(x2 / (x2 + 1))
        if x2 is None:
            x2 = x.multiply(x)
        ones_where_nonzero = x2.sign()
        return self.coef * 0.5 * (x2 / (x2 + ones_where_nonzero)).sum()

    def prox(x, lr=None):
        raise NotImplementedError('Exact proximal operator for bounded l2 does not exist. Consider using gradients.')

    def grad(self, x):
        if not scipy.sparse.issparse(x):
            return self.coef * x / (x**2+1)**2
        ones_where_nonzero = abs(x.sign())
        x2_plus_one = x.multiply(x) + ones_where_nonzero
        denominator = x2_plus_one.multiply(x2_plus_one)
        return self.coef * x.multiply(ones_where_nonzero / denominator)

    @property
    def smoothness(self):
        return self.coef


import numpy as np
import matplotlib.pyplot as plt
from numpy.fft import fft,fftfreq, ifft

def main():
	#setup domain
	#number of points
	n=int(input("Enter sampling rate: "))

	#time length in secs
	l= int(input("Enter time lenght in seconds, the time for which you want to perform the fft"))

	#angular frequency
	omg=2*np.pi/l

	#creating signals
	x=np.linspace(0,l,n);
	y1=1*np.cos(5*omg*x)
	y2=1*np.cos(10*omg*x)
	y3=0.5*np.cos(20*omg*x)

	y=y1+y2+y3

	freq=fftfreq(n)

	mask=freq>0

	fft_vals=fft(y)

	fft_theo=2*np.abs(fft_vals/n)

	plt.figure(1)
	plt.title('Original Image')
	plt.plot(x,y,color='xkcd:salmon',label='Original')
	plt.legend()

	plt.figure(2)
	plt.title('FFT')
	#plt.plot(freq,fft_vals,label='fft')
	#plt.title('True FFT')
	plt.plot(freq[mask],fft_theo[mask],label='true fft values')
	plt.show()


import numpy as np

toInt = lambda x: list(map(int, x.strip().split()))

arr = np.array([toInt(input()) for _ in range(toInt(input())[0])])

print(np.mean(arr, axis=1))
print(np.var(arr, axis=0))

print(np.round(np.std(arr), 11))

# 0.82915619759
# 0.82915619758885

python_solutions/numpy/Mean, Var, and Std.py


import sys
import math
from array import *
import numpy as np

n = int(sys.argv[1])
k = int(sys.argv[2])

P=[[0 for i in range(k+1)] for j in range(n+1)] # initialize

for i in range(n+1):
    for j in range(k+1):
        if j == 0 or i == j:
            P[i][j] = 1
        else:
            P[i][j] = P[i-1][j-1] + P[i-1][j]

r = P[n][k]

print(str(n) + " choose " + str(k) + " = " + str(r))


import scipy as sp
import scipy.linalg as linalg
from timeit import timeit

def DoolittleLU(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    l = sp.zeros((n, n), dtype=dtype)
    u = sp.zeros((n, m), dtype=dtype)

    for i in range(n):
        for k in range(i, m):
            s = 0
            for j in range(i):
                s += l[i][j] * u[j][k]

            u[i][k] = matrix[i][k] - s

        l[i][i] = 1
        for k in range(i+1, n):
            s = 0
            for j in range(i):
                s += l[k][j] * u[j][i]
            l[k][i] = (matrix[k][i] - s) / u[i][i]

    return l, u

def CroutLU(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    l = sp.zeros((n, m))
    u = sp.zeros((m, m))

    for j in range(m):
        for i in range(j, n):
            s = 0
            for k in range(j):
                s += l[i][k] * u[k][j]
            l[i][j] = matrix[i][j] - s

        u[j][j] = 1
        for i in range(j+1, m):
            s = 0
            for k in range(j):
                s += l[j][k] * u[k][i]
            u[j][i] = (matrix[j][i] - s) / l[j][j]

    return l, u

def LUDecomposition(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    L = sp.identity(n, dtype)
    U = sp.array(matrix, dtype=dtype)

    for i in range(1, n):
        for j in range(i):
            L[i][j], U[i][j] = U[i][j]/U[j][j], dtype(0)
            for k in range(j+1, m):
                U[i][k] -= U[j][k]*L[i][j]

    return L, U

def LUPPDecomposition(matrix: sp.ndarray, dtype=float):
    n, _ = matrix.shape
    absmatrix = sp.absolute(matrix)
    scales = sp.amax(absmatrix, axis=1)
    p = sp.arange(n)
    p = sorted(p, key=lambda i: absmatrix[i][0]/scales[i], reverse=True)

    P = sp.zeros((n, n))
    for idx, pivot in enumerate(p):
        P[idx][pivot] = 1

    L, U = LUDecomposition(P@matrix, dtype)
    return P.transpose(), L, U

def LUCPDecomposition(matrix: sp.ndarray, dtype=float):
    n, m = matrix.shape
    absmatrix = sp.absolute(matrix)
    rowscales = sp.amax(absmatrix, axis=1)
    colscales = sp.amax(absmatrix, axis=0)
    p = sp.arange(n)
    p = sorted(p, key=lambda i: absmatrix[i][0]/rowscales[i], reverse=True)
    q = sp.arange(m)
    q = sorted(q, key=lambda j: absmatrix[0][j]/colscales[j], reverse=True)

    P = sp.zeros((n, n))
    for idx, pivot in enumerate(p):
        P[idx][pivot] = 1
    Q = sp.zeros((m, m))
    for idx, pivot in enumerate(q):
        Q[idx][pivot] = 1

    L, U = LUDecomposition(P@matrix@Q, dtype)

    return P.transpose(), L, U, Q.transpose()

def CholeskyDecomposition(matrix: sp.ndarray, dtype=float):
    n, _ = matrix.shape
    L = sp.zeros((n, n))

    for i in range(n):
        for j in range(i):
            L[i][j] = matrix[i][j]
            for k in range(j):
                L[i][j] -= L[i][k] * L[j][k]
            L[i][j] /= L[j][j]
        L[i][i] = matrix[i][i]
        for k in range(i):
            L[i][i] -= L[i][k] ** 2
        L[i][i] **= 0.5

    return L

def LDLTDecomposition(matrix: sp.ndarray, dtype=float):
    L, U = LUDecomposition(matrix, dtype)

    return L, sp.diag(sp.diag(U))

def AssertLU(matrix: sp.ndarray, msg=''):
    print(msg)
    P, L, U = linalg.lu(matrix)
    if sp.allclose(matrix, P@L@U, rtol=0):
        print('PASS: linalg.lu')
        print(
            f'time: {timeit(lambda: linalg.lu(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: linalg.lu')

    L, U = DoolittleLU(matrix)
    if sp.allclose(matrix, L@U, rtol=0):
        print('PASS: DoolittleLU')
        print(
            f'time: {timeit(lambda: DoolittleLU(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: DoolittleLU')

    L, U = CroutLU(matrix)
    if sp.allclose(matrix, L@U, rtol=0):
        print('PASS: CroutLU')
        print(
            f'time: {timeit(lambda: CroutLU(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: CroutLU')

    L, U = LUDecomposition(matrix)
    if sp.allclose(matrix, L@U, rtol=0):
        print('PASS: LU decomposition')
        print(
            f'time: {timeit(lambda: LUDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: LU decomposition')

    P, L, U = LUPPDecomposition(matrix)
    if sp.allclose(matrix, P@L@U, rtol=0):
        print('PASS: LUPP decomposition')
        print(
            f'time: {timeit(lambda: LUPPDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: LUPP decomposition')

    P, L, U, Q = LUCPDecomposition(matrix)
    if sp.allclose(matrix, P@L@U@Q, rtol=0):
        print('PASS: LUCP decomposition')
        print(
            f'time: {timeit(lambda: LUCPDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
    else:
        print('FAIL: LUCP decomposition')

    # Symmetric matrix only
    if matrix.shape[0] == matrix.shape[1] and sp.allclose(matrix, sp.transpose(matrix), rtol=0):
        L, D = LDLTDecomposition(matrix)
        if sp.allclose(matrix, L@D@L.transpose(), rtol=0):
            print('PASS: LDLT decomposition')
            print(
                f'time: {timeit(lambda: LDLTDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
        else:
            print('FAIL: LDLT decomposition')

        L = CholeskyDecomposition(matrix)
        if sp.allclose(matrix, L@L.transpose(), rtol=0):
            print('PASS: Cholesky decomposition')
            print(
                f'time: {timeit(lambda: CholeskyDecomposition(matrix), number=1000) * 1000: 3.0f}ms')
        else:
            print('FAIL: Cholesky decomposition')

    print()

def main():
    A = sp.array([[1, 1, 1],
                  [1, 2, 4],
                  [1, 3, 9]])
    AssertLU(A, 'Low rank(3) Vandermonde matrix')

    A = sp.array([[-1]])
    AssertLU(A, 'Single element matrix')

    A = sp.array([[3, 2, 4],
                  [2, 4, 3]])
    AssertLU(A, 'Non-square(2, 3) matrix')

    A = sp.array([[2, 4],
                  [3, 3],
                  [4, 2]])
    AssertLU(A, 'Non-square(3, 2) matrix')

    A = sp.array([[1, -2, 0],
                  [-2, 1, -2],
                  [0, -2, 1]])
    AssertLU(A, 'Symmetric matrix')

    A = sp.array([[4, 12, -16],
                  [12, 37, -43],
                  [-16, -43, 98]])
    AssertLU(A, 'Positive-definite matrix')

    A = sp.array([[1, 2, 3],
                  [2, 4, 7],
                  [3, 3, 3]])
    AssertLU(A, 'Bad condition of naive LU decomposition')

    A = sp.array([[1, 9, 3],
                  [2, 2, 7],
                  [3, 3, 3]])
    AssertLU(A, 'Bad condition of LUPP decomposition')

    A = sp.zeros((16, 16))
    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            A[i][j] = (i+1)**j
    AssertLU(A, 'High rank(16) Vandermonde matrix')

    A = sp.zeros((17, 17))
    for i in range(A.shape[0]):
        for j in range(A.shape[1]):
            A[i][j] = (i+1)**j
    AssertLU(A, 'High rank(17) Vandermode matrix')

    A = sp.array([[1, 1, 1],
                  [2, 2, 2],
                  [3, 3, 3]])
    AssertLU(A, 'Singular matrix')

    A = sp.rand(50, 50)
    AssertLU(A, 'Big(50, 50) matrix')

if __name__ == '__main__':
    main()


import numpy as np

class SoftmaxClassifier:
    def __init__(self):
        # Random initialization of W
        C, D = 10, 3073  # C :number of classes, D: dimension of each flattened image
        self.W = np.random.randn(C, D) * 0.001

    def train(self, x, y, lr=1e-5, reg=1e-3, num_iters=1000, batch_size=200):
        """
        Train the model using stochastic gradient descent.
        Arguments:
            x: D * N numpy array as the training data, where D is the dimension and N the training sample size
            y: 1D numpy array with length N as the labels for the training data
            lr: learning rate
            reg: regularization factor
            num_iters: number of iterations for training
            batch_size: batch size for the calculation of the mini-batch gradient descent
        Output:
            loss_record: Array containing the cross entropy loss history during the training process
        """
        _, N = x.shape  # N is the sample size

        # Train the model using mini-batch stochastic gradient descent.
        loss_record = []
        for it in range(num_iters):
            # Randomly chosen mini-batch
            indices = np.random.choice(N, batch_size, replace=True)
            x_batch = x[:, indices]
            y_batch = y[indices]

            # Calculate loss and gradient for the iteration
            loss, grad = self.cross_entropy_loss(x_batch, y_batch, reg)
            loss_record.append(loss)

            # Update W
            self.W -= lr * grad

        return loss_record

    def predict(self, x):
        """
        Predict labels using the trained model.
        Arguments:
            x: D * N numpy array as the test data, where D is the dimension and N the test sample size
        Output:
            y_pred: 1D numpy array with length N as the predicted labels for the test data
        """
        y = self.W.dot(x)
        y_pred = np.argmax(y, axis=0)
        return y_pred

    def cross_entropy_loss(self, x, y, reg):
        """
        Calculate the cross-entropy loss and the gradient for each iteration of training.
        Arguments:
            x: D * N numpy array as the training data, where D is the dimension and N the training sample size
            y: 1D numpy array with length N as the labels for the training data
        Output:
            loss: a float number of calculated cross-entropy loss
            dW: C * D numpy array as the calculated gradient for W, where C is the number of classes, and 10 for this model
        """

        # Calculation of loss
        z = np.dot(self.W, x)
        z -= np.max(z, axis=0)  # Max trick for the softmax, preventing infinite values
        p = np.exp(z) / np.sum(np.exp(z), axis=0)  # Softmax function
        L = -1 / len(y) * np.sum(np.log(p[y, range(len(y))]))  # Cross-entropy loss
        R = 0.5 * np.sum(np.multiply(self.W, self.W))  # Regularization term
        loss = L + R * reg  # Total loss

        # Calculation of dW
        p[y, range(len(y))] -= 1
        dW = 1 / len(y) * p.dot(x.T) + reg * self.W
        return loss, dW


# coding=utf-8
"""
If p is the perimeter of a right angle triangle with integral length sides, {a,b,c}, there are exactly three solutions for p = 120.

{20,48,52}, {24,45,51}, {30,40,50}

For which value of p ≤ 1000, is the number of solutions maximised?

Solution comment:
Assume a < b, and know b < c. Then a <= p/2, a < b <= (p-a)/2.

Then simply check if c^2 = a^2 + b^2 is possible and makes the
total perimeter equal to p.
"""
from math import sqrt
from gmpy2 import is_square
from numpy import zeros, argmax

p_all = xrange(3, 1000 + 1)
solutions = zeros(len(p_all))

for i, p in enumerate(p_all):
    for a in xrange(1, p//2+1):
        for b in xrange(a+1, (p - a)//2 + 1):
            c2 = a**2 + b**2
            if is_square(c2) and int(sqrt(c2)) + a + b == p:
                solutions[i] += 1

print p_all[argmax(solutions)]


import numpy as np
from distance import euclidean
from kmeans import KMeans

def elbow_method(X, k_range = range(1,9), init_centroid = "random", distance="euclidean"):
  X = np.array(X)
  distortion_scores = []
  silhouete_scores = []
  for k in k_range:
    model = KMeans(k = k, init_centroid=init_centroid, distance=distance)
    model.train(X)
    distortion_scores.append(model.inertia)
    silhouete_scores.append(silhouete_score(X, model.centroids))
  return distortion_scores, silhouete_scores

def silhouete_coef(x, centroids):
  distances = []
  if len(centroids) == 1 :
    return 0
  for centroid in centroids:
    distances.append(euclidean(x, centroid))
  distances.sort()
  return (distances[1] - distances[0]) / max(distances[0], distances[1])

def silhouete_score(X, centroids):
  X=np.array(X)
  silhouete_coefs = []
  for point in X:
    silhouete_coefs.append(silhouete_coef(point, centroids))
  return sum(silhouete_coefs) / len(silhouete_coefs)


from sympy.core import S, C, sympify, cacheit
from sympy.core.function import Function, ArgumentIndexError
from sympy.functions.elementary.miscellaneous import sqrt

###############################################################################
################################ ERROR FUNCTION ###############################
###############################################################################

class erf(Function):

    nargs = 1

    def fdiff(self, argindex=1):
        if argindex == 1:
            return 2*C.exp(-self.args[0]**2)/sqrt(S.Pi)
        else:
            raise ArgumentIndexError(self, argindex)

    @classmethod
    def eval(cls, arg):
        if arg.is_Number:
            if arg is S.NaN:
                return S.NaN
            elif arg is S.Infinity:
                return S.One
            elif arg is S.NegativeInfinity:
                return S.NegativeOne
            elif arg is S.Zero:
                return S.Zero
            elif arg.is_negative:
                return -cls(-arg)
        elif arg.is_Mul:
            if arg.as_coeff_mul()[0].is_negative:
                return -cls(-arg)

    @staticmethod
    @cacheit
    def taylor_term(n, x, *previous_terms):
        if n < 0 or n % 2 == 0:
            return S.Zero
        else:
            x = sympify(x)

            k = (n - 1)//2

            if len(previous_terms) > 2:
                return -previous_terms[-2] * x**2 * (n-2)/(n*k)
            else:
                return 2*(-1)**k * x**n/(n*C.Factorial(k)*sqrt(S.Pi))

    def _eval_as_leading_term(self, x):
        arg = self.args[0].as_leading_term(x)

        if C.Order(1,x).contains(arg):
            return arg
        else:
            return self.func(arg)

    def _eval_is_real(self):
        return self.args[0].is_real


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Blackbody functions for wavelength and frequency.

In wavelength space, the blackbody function assumes that the wavelength
is given in units of Angstroms.
"""

import numpy as np
from numba import jit

from pypython.constants import (ANGSTROM, BOLTZMANN, VLIGHT, WIEN_FREQUENCY, WIEN_WAVELENGTH, H, PI, STEFAN_BOLTZMANN)

@jit
def planck_lambda(temperature, lamda):
    """Calculate the monochromatic intensity for a black body given a
    temperature and frequency of interest.

    Parameters
    ----------
    temperature: float
        The temperature of the blackbody.
    lamda: np.ndarray or float
        The wavelength points to calculate the value at, in Angstroms.

    Returns
    -------
    b_lamda: float
        The value of the Planck function with the provided temperature and
        wavelength. Has units ergs s^-1 cm^-2 A^-1.
    """

    lcm = lamda * ANGSTROM
    x = H * VLIGHT / lcm / BOLTZMANN / temperature
    y = 2 * H * VLIGHT**2 / lcm**5
    b_lamda = y / (np.exp(x) - 1)

    return b_lamda

@jit
def planck_nu(temperature, frequency):
    """Calculate the monochromatic intensity for a black body given a
    temperature and frequency of interest.

    Parameters
    ----------
    temperature: float
        The temperature of the blackbody.
    frequency: np.ndarray or float
        The frequency points to calculate the vale at, in units of Hz.

    Returns
    -------
    b_nu: float
        The value of the Planck function with the provided temperature and
        frequency. Has units ergs s^-1 cm^-2 Hz^-1.
    """

    x = H * frequency / (BOLTZMANN * temperature)
    b_nu = (2 * H * frequency**3) / (VLIGHT**2 * (np.exp(x) - 1))

    return b_nu

def stefan_boltzmann(radius, temperature):
    """Calculate the luminosity for a spherical blackbody following from
    Stefan-Boltzmann.

    Parameters
    ----------
    radius: float
        The radius of the sphere.
    temperature: float
        The temperature of the blackbody.

    Returns
    -------
    lum: float
        The luminosity of the sphere.
    """
    return 4 * PI * radius**2 * STEFAN_BOLTZMANN * temperature**4

def wien_law(temperature, freq_space=False):
    """Calculate the peak wavelength of a blackbody curve.

    Parameters
    ----------
    temperature: float
        The temperature of the blackbody.
    freq_space: bool [optional]
        Return the peak in frequency space.

    Returns
    -------
    The wavelength (in Angstrom) or frequency where the blackbody curve is
    at maximum.
    """

    if freq_space:
        return WIEN_FREQUENCY * temperature
    else:
        return WIEN_WAVELENGTH / temperature / 1e-10


#This file contains implementation of Principal Component Analysis
import numpy as np

class PCA:
    def __init__(self, n_components):
    """Function used to initialize variables
    Args:
        n_components: Input parameter
    Returns:
        None
    """
        self.n_components = n_components
        self.components = None
        self.mean = None

    def fit(self, X):
        #Compute mean
        self.mean = np.mean(X, axis = 0)
        X = X- self.mean
        #Compute Covariance
        cov  = np.cov(X.T)
        #Eigen vectors and eigenvalues
        eigenvalues, eigenvectors = np. linalg.eig(cov)
        #Sort Eigenvectors
        eigenvectors = eigenvectors.T
        idxs = np.argsort(eigenvalues)[::-1]
        eigenvalues = eigenvalues[idxs]
        eigenvectors = eigenvectors[idxs]
        #Store first n in eigenvectors
        self.components = eigenvectors[0:self.n_components]

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

if __name__ == "__main__":
    # Imports
    import matplotlib.pyplot as plt
    from sklearn import datasets

    # data = datasets.load_digits()
    data = datasets.load_iris()
    X = data.data
    y = data.target

    # Project the data onto the 2 primary principal components
    pca = PCA(2)
    pca.fit(X)
    X_projected = pca.transform(X)

    print("Shape of X:", X.shape)
    print("Shape of transformed X:", X_projected.shape)

    x1 = X_projected[:, 0]
    x2 = X_projected[:, 1]

    plt.scatter(
        x1, x2, c=y, edgecolor="none", alpha=0.8, cmap=plt.cm.get_cmap("viridis", 3)
    )

    plt.xlabel("Principal Component 1")
    plt.ylabel("Principal Component 2")
    plt.colorbar()
    plt.show(


from scipy.spatial.distance import minkowski
import numpy as np

import matplotlib
import matplotlib.pyplot as plt

from numpy import linalg as LA
from math import sqrt

def euclidean_distance(spectrum_A,spectrum_B,power_value):
    spectrum_A=np.array(spectrum_A).flatten()
    spectrum_B=np.array(spectrum_B).flatten()

    #normalize according to the maximum value
    #max_A=max(spectrum_A)
    #max_B=max(spectrum_B)
    #print max_A,max_B
    #spectrum_A=np.array([x/max_A for x in spectrum_A.flatten()]).flatten()
    #spectrum_B=np.array([x/max_B for x in spectrum_B.flatten()]).flatten()

    #Normalize according to modulus of spectrum vectors
    norm_A=LA.norm(spectrum_A)
    norm_B=LA.norm(spectrum_B)
    spectrum_A=np.array([x/norm_A for x in spectrum_A.flatten()]).flatten()
    spectrum_B=np.array([x/norm_B for x in spectrum_B.flatten()]).flatten()

    eucl_metric=LA.norm(np.subtract(spectrum_A,spectrum_B))

    eucl_metric=np.power(eucl_metric,power_value)

    return 1.-eucl_metric

def cosine_sim_given_spectra(spectrum_A,spectrum_B,power_value=1.):

    spectrum_A=np.array(spectrum_A).flatten()
    spectrum_B=np.array(spectrum_B).flatten()

    dot_product=np.power(np.dot(spectrum_A,spectrum_B),power_value)

    norm_A=sqrt(np.power(np.dot(spectrum_A,spectrum_A),power_value))
    norm_B=sqrt(np.power(np.dot(spectrum_B,spectrum_B),power_value))
    """
    norm_A=LA.norm(spectrum_A)
    norm_B=LA.norm(spectrum_B)
    """
    kernel=dot_product/(norm_A*norm_B)
    return kernel

def plot_2D_matrix_with_axis_labels(matrix, x_axis_labels, y_axis_labels, title, savefig_name, save_fig_type,vmax,vmin,cmap=matplotlib.cm.get_cmap('Purples'), show_sim_values=True, fontsize=5):

    matrix=np.array(matrix)

    if vmax==None:
        vmax=max(np.array(matrix).flatten())
    if vmin==None:
        vmin=min(np.array(matrix).flatten())

    x_pos=np.arange(len(x_axis_labels))
    y_pos=np.arange(len(y_axis_labels))

    #plt.tight_layout()
    #fig, ax = plt.subplots()

    plt.imshow(matrix,vmax=vmax,vmin=vmin,cmap=cmap)
    plt.colorbar()

    #ax.xaxis.set_ticks_position('bottom') # need to call this after matshow, otherwise error
    plt.xticks(x_pos,x_axis_labels, rotation=90, fontsize=fontsize)
    plt.yticks(y_pos,y_axis_labels, fontsize=fontsize)
    plt.title(title, y=1.1)

    #Add text
    if show_sim_values:
        fmt = '.2f'
        thresh = max(matrix.flatten()) * 0.5
        for i, j in itertools.product(range(matrix.shape[0]), range(matrix.shape[1])):
            plt.text(j, i, format(matrix[i, j], fmt),
                     horizontalalignment="center",
                     color="white" if matrix[i, j] > thresh else "black", fontsize=fontsize)

    plt.tight_layout()
    plt.show()
    plt.savefig(savefig_name+save_fig_type)
    plt.close()

def similarity_metric(spectrum_A,spectrum_B,metric,power_value,p=3):
    """
    ARGUMENTS: spectrum_A,spectrum_B: signals/spectra to be compared
               metric: similarity metric to be used (possible choices: 'cosine_sim','euclidean','cross_correlation' (TODO last one incomplete))
               power_value: power to which result of similarity metric is raised

    RETURNS:   value of similarity metric

    """

    spectrum_A=np.array(spectrum_A).flatten()
    spectrum_B=np.array(spectrum_B).flatten()

    if metric=='cosine_sim':

        return cosine_sim_given_spectra(spectrum_A,spectrum_B,power_value)

    elif metric=='euclidean':

        return euclidean_distance(spectrum_A,spectrum_B,power_value)

    elif metric=='minkowski':
        zero_vector=np.zeros(spectrum_A.size)

        norm_A=minkowski(u=spectrum_A,v=zero_vector,p=p,w=None)
        norm_B=minkowski(u=spectrum_B,v=zero_vector,p=p,w=None)

        spectrum_A_normed=spectrum_A/norm_A
        spectrum_B_normed=spectrum_B/norm_B

        minkowski_AB=minkowski(u=spectrum_A_normed,v=spectrum_B_normed,p=p,w=None)
        return 1.-minkowski_AB

    elif metric=='cross_correlation':
        #Not sure if this doesnt give the same as skp since automatic mode is 'valid' .... https://docs.scipy.org/doc/numpy/reference/generated/numpy.correlate.html
        corr_A_B=np.correlate(spectrum_A,spectrum_B)
        corr_A_A=np.correlate(spectrum_A,spectrum_A)
        corr_B_B=np.correlate(spectrum_B,spectrum_B)

        normalized_corr=corr_A_B/(corr_A_A*corr_B_B)

        return np.power(normalized_corr,power_value)

    else:

        raise NotImplementedError("The chosen metric is not implemented.")

def compute_and_plot_cross_similarity(descriptors_A,descriptors_B,sim_metric,power_value,p,x_axis_labels,y_axis_labels,
                                      title,savefig_name,save_fig_type='.png',
                                      vmax=None, vmin=None,
                                      cmap=matplotlib.cm.get_cmap('Purples'), show_sim_values=True,
                                      fontsize=5):

    sim_matrix=[]

    for descriptor_1 in descriptors_A:

        sim_matrix_row=[]

        for descriptor_2 in descriptors_B:

            #coherence_matrix_row.append(coherence_kernel(descriptor_1,descriptor_2))
            sim_matrix_row.append(similarity_metric(descriptor_1,descriptor_2,sim_metric,power_value,p))

        #coherence_matrix.append(coherence_matrix_row)
        sim_matrix.append(sim_matrix_row)

    plot_2D_matrix_with_axis_labels(sim_matrix, x_axis_labels, y_axis_labels, title, savefig_name, save_fig_type, vmax, vmin, cmap, show_sim_values, fontsize)

    return sim_matrix


import tensorflow as tf
import numpy as np

sess = tf.Session()
sess.run(tf.global_variables_initializer())

a_2x3 = tf.constant([[1, 2, 3], [4, 5, 6]])
b_vec3 = tf.constant([10, 20, 30])

# This makes no sense mathematically
sum = a_2x3 + b_vec3

# In order to work around this, TensorFlow performs "broadcasting"
# turning the 3-vector into a matrix of two rows each with the elements
# of the 3-vector and applying the sum to that.

print(sess.run(sum, {}))

# [[11 22 33]
# [14 25 36]]

# Broadcasting with two matrices with different number of rows
# Like for the vector example above, the rows of the smaller one are replicated to the height of the other

b_1x3 = tf.constant([[10, 20, 30]])
print(sess.run(a_2x3 + b_1x3, {}))

# [[11 22 33]
# [14 25 36]]


"""
Author      :   Muhammad Reza Fahlevi
Dated       :   June 7th, 2021
Affiliation :   Departemen Ilmu Komputer,
                Fakultas Ilmu Komputer - Teknologi Informasi,
                Universitas Sumatera Utara, Indonesia
References  :   Benjamin Baka. Python Data Structures and Algorithms. 2017. Packt Publishing.
                García-Martínez C., Rodriguez F.J., Lozano M. (2018) Genetic Algorithms. In: Martí R., Pardalos P., Resende M. (eds) Handbook of Heuristics. Springer, Cham. https://doi.org/10.1007/978-3-319-07124-4_28
                https://docs.python.org/3.8/tutorial/index.html
                J M García et al 2020 J. Phys.: Conf. Ser. 1448 012020
                Stuart Russell and Peter Norvig. 2020. Artificial Intelligence: A Modern Approach, 4th Edition. University of California at Berkeley. Pearson.
"""

# Prepare the tools
import math
import pandas as pd
from numpy import random

# phi = lambda x,y: math.exp(-0.5 * ((x ** 2) + (y ** 2)))

def phi(x, y):
    first_denominator = 1 + ((x - 1) ** 2) + ((y - 1) ** 2)
    second_denominator = 1 + (1/4) * (((x + 0.5) ** 2)) + (1/36) * ((y - 1) ** 2)
    third_denominator = 1 + ((x - 2) ** 2) + ((y - 2) ** 2)
    fourth_denominator = 1 + ((x - 1) ** 2) + ((y + 1) ** 2)
    exp_term = (3 / 2) * math.exp(1 / first_denominator) + (-5 / 2) * math.exp(1 / second_denominator) + 2 * math.exp(1 / third_denominator) + 2 * math.exp(1 / fourth_denominator)
    return exp_term

xvect, yvect = random.random(10), random.random(10)
print(xvect, yvect)
print([phi(x, y) for x, y in zip(xvect, yvect)])

def competition(df_pop):
    st_candidate = df_pop.iloc[random.randint(9), 0:3]
    nd_candidate = df_pop.iloc[random.randint(9), 0:3]
    winner = st_candidate if st_candidate['fitness'] > nd_candidate['fitness'] else nd_candidate
    return winner

def pbxalpha(domain_interval, p1, p2, alpha):
    constant = abs(p1['xgenotype'] - p2['xgenotype'])
    p1_lower_bound = max(domain_interval[0], p1['xgenotype'] - (alpha * constant))
    p1_upper_bound = min(domain_interval[1], p1['xgenotype'] + (alpha* constant))

    constant = abs(p1['ygenotype'] - p2['ygenotype'])
    p2_lower_bound = max(domain_interval[0], p2['ygenotype'] - (alpha * constant))
    p2_upper_bound = min(domain_interval[1], p2['ygenotype'] + (alpha* constant))

    p1_offspring = [p1_lower_bound, p1_upper_bound]
    p2_offspring = [p2_lower_bound, p2_upper_bound]

    return p1_offspring, p2_offspring

gamma = lambda t, tmax, beta: (1 - (t / tmax)) ** beta

def delta(domain_interval, genotype, computed_gamma):
    get_probability = random.random()
    probability_criteria = 1 / 2
    normvar = random.random()
    if get_probability == probability_criteria:
        delta_value = (domain_interval[1] - genotype) * ((1 - normvar) ** computed_gamma)
    else:
        delta_value = (domain_interval[0] - genotype) * ((1 - normvar) ** computed_gamma)
    return delta_value

# Define the constant
NP = 10
Pm = 0.3
MAX_GENERATION = 50
problem_interval = [-5, 5]

def config_df(x_vect, y_vect):
    dframe = pd.DataFrame(
        {
            "xgenotype": x_vect,
            "ygenotype": y_vect,
            "fitness": [phi(x, y) for x, y in zip(x_vect, y_vect)]
        }, index = [(idx + 1) for idx in range(len(x_vect))]
    )
    data_frame = dframe.sort_values('fitness')
    data_frame = data_frame.iloc[1:10]
    return data_frame

def steady_state_genetic_algorithm(population_size, mutation_probability, max_generation):
    generations = []
    x_vector = random.random(population_size)
    y_vector = random.random(population_size)

    for generation in range(max_generation):
        df_population = config_df(x_vector, y_vector)
        generations.append(df_population)
        x_vector = [] # start a new population
        y_vector = []
        x_vector.append(df_population.iloc[population_size - 2, 0]) # add the fittest to the next generation
        y_vector.append(df_population.iloc[population_size - 2, 1])
        vect_size = len(x_vector)
        while vect_size != population_size:
            p1, p2 = competition(df_population), competition(df_population) # Selection
            offsprings = pbxalpha(problem_interval, p1, p2, alpha=0.5) # Crossover, return tuple of list
            current_mutation_rate = random.random()
            if current_mutation_rate < mutation_probability:
                p11 = offsprings[0][0] + delta(problem_interval, offsprings[0][0], gamma(generation, max_generation, beta=9))
                p12 = offsprings[0][1] + delta(problem_interval, offsprings[0][1], gamma(generation, max_generation, beta=9))
                p21 = offsprings[1][0] + delta(problem_interval, offsprings[1][0], gamma(generation, max_generation, beta=9))
                p22 = offsprings[1][1] + delta(problem_interval, offsprings[1][1], gamma(generation, max_generation, beta=9))
                offsprings = tuple([[p11, p12], [p21, p22]]) # mutation occur, return tuple of list
            else:
                offsprings = offsprings # no mutation occurs
            # Evaluation
            first_value = phi(offsprings[0][0], offsprings[0][1])
            second_value = phi(offsprings[1][0], offsprings[1][1])
            survivor = [offsprings[0][0], offsprings[0][1]] if first_value > second_value else [offsprings[1][0], offsprings[1][1]]
            x_vector.append(survivor[0])
            y_vector.append(survivor[1])
            vect_size += 1
    return generations

for i in range(25):
    print(steady_state_genetic_algorithm(NP, Pm, MAX_GENERATION)[MAX_GENERATION - 1])


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

def model(inputs, W, b):
    """
    This is a linear classifier model.
    :param inputs:
    :param W:
    :param b:
    :return:
    """

    outputs = tf.matmul(inputs, W) + b
    return outputs

def square_loss(true_labels, predictions):
    """
    This function gets the loss of model based on predictions and labels.
    :param true_labels:
    :param predictions:
    :return:
    """

    loss_per_sample = tf.square(predictions - true_labels)
    return tf.reduce_mean(loss_per_sample)

def fit(X, y, W, b, learning_rate=0.1):
    """
    Fit the model.
    :param X:
    :param y:
    :param W:
    :param b:
    :param learning_rate:
    :return:
    """

    with tf.GradientTape() as tape:
        predictions = model(inputs=X, W=W, b=b)
        loss = square_loss(true_labels=y, predictions=predictions)

    gradients_dW, gradients_db = tape.gradient(target=loss, sources=[W, b])  # Get gradients
    W.assign_sub(gradients_dW * learning_rate)  # Update W
    b.assign_sub(gradients_db * learning_rate)  # Update b

    return loss, W, b

# Generate random data
num_samples_per_class = 1000
negative_samples = np.random.multivariate_normal(mean=[0, 3], cov=[[1, 0.5], [0.5, 1]],
                                                 size=num_samples_per_class)
negative_labels = np.zeros(shape=(num_samples_per_class, 1))

positive_samples = np.random.multivariate_normal(mean=[3, 0], cov=[[1, 0.5], [0.5, 1]],
                                                 size=num_samples_per_class)
positive_labels = np.ones(shape=(num_samples_per_class, 1))

X = np.vstack((negative_samples, positive_samples)).astype('float32')
y = np.vstack((negative_labels, positive_labels)).astype('float32')

# Initialize the model parameters
input_dims = 2
output_dims = 1
W = tf.Variable(initial_value=tf.random.uniform(shape=(input_dims, output_dims)))
b = tf.Variable(initial_value=tf.zeros(shape=(output_dims,)))

# Train model
epochs = 50
loss = np.inf

for i_epoch in range(epochs):
    loss, W, b = fit(X=X, y=y, W=W, b=b)

    print(f'Loss at step {i_epoch}: {loss:.4f}')

# Get predictions
predictions = model(inputs=X, W=W, b=b)

# Plot results
x = np.linspace(-1, 4, 100)

weights = W.numpy()
bias = b.numpy()
hyper_line = -weights[0] / weights[1] * x + (0.5 - bias) / weights[1]
plt.plot(x, hyper_line, '-r')
plt.scatter(X[:, 0], X[:, 1], c=predictions[:, 0] >= 0.5)
plt.show()


import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.preprocessing import normalize

corpus = ["dog bites man", "man bites dog", "dog eats meat", "man eats food"]
#['bites', 'dog', 'eats', 'food', 'man', 'meat']
tfidf = TfidfVectorizer()
output = tfidf.fit_transform(corpus)
#print(sorted(tfidf.vocabulary_, key=tfidf.vocabulary_.get), tfidf.idf_)
print(tfidf.get_feature_names(), tfidf.idf_)
print(output.toarray())
pred = tfidf.transform(["dog man dog"]) #with word frequency 2
print(pred.toarray())

## manual calculation
dog_idf = 1 + np.log((1+4)/(1+3)) # add 1 to num & denom
bites_idf = 1 + np.log((1+4)/(1+2))
man_idf = 1 + np.log((1+4)/(1+3))
print(normalize([[bites_idf, dog_idf, 0, 0, man_idf, 0]])) #default is l2 norm


"""
Purpose: To show an example of principal component analysis (PCA)
for doing linear dimension reduction on a collection of image
patches.  Each patch can be thought of as a point in high dimensional
Euclidean space, where each dimension in that space is a pixel, and
the value along that axis is the brightness of that pixel.
White pixels have 255 as their value, black pixels have 0 as their value,
and gray pixels are somewhere in between.

When you do the dimension reduction with numbers that are very different,
you should see them clustering into two distinct regions.  In this example,
we show that 3s and 0s end up in different parts of the space
"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.metrics import pairwise_distances
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import skimage
from sklearn.decomposition import PCA

def imscatter(X, P, dim, zoom=1):
    """
    Plot patches in specified locations in 2D

    Parameters
    ----------
    Y : ndarray (N, 2)
        The positions of each patch in 2D
    P : ndarray (N, dim*dim)
        An array of all of the patches
    dim : int
        The dimension of each patch

    """
    #https://stackoverflow.com/questions/22566284/matplotlib-how-to-plot-images-instead-of-points
    ax = plt.gca()
    for i in range(P.shape[0]):
        patch = np.reshape(P[i, :], (dim, dim))
        x, y = Y[i, :]
        im = OffsetImage(patch, zoom=zoom, cmap = 'gray')
        ab = AnnotationBbox(im, (x, y), xycoords='data', frameon=False)
        ax.add_artist(ab)
    ax.update_datalim(X)
    ax.autoscale()
    ax.set_xticks([])
    ax.set_yticks([])

# Steup an array that will hold 200 images in 784 dimensions (28x28 pixels)
X = np.zeros((200, 784))

# Load in all of the zeros into the first 100 rows
for i in range(100):
    x = skimage.io.imread("Digits/0/{}.png".format(i))
    x = x.flatten()
    X[i, :] = x # Put image i into row i

# Load in all of the threes into the next 100 rows
for i in range(100):
    x = skimage.io.imread("Digits/3/{}.png".format(i))
    x = x.flatten()
    X[100+i, :] = x # Put image i into row i

# This is an example of what pairwise_distances is actually
# doing to compute distances between each pair of points in X.
# In this case, we're computing Euclidean distance between
# the point at index 10 and the point at index 20
d_10_20 = np.sqrt(np.sum(X[10, :] - X[20, :])**2)

# But it is much faster to use the pairwise_distances function
# to do all of them
# When you run this code, you should see that most of the
# 0s are closer to each other than they are to the 3s, and
# vice versa
D = pairwise_distances(X)
plt.figure()
plt.imshow(D, cmap='magma_r')
plt.xticks([50, 150], ["0s", "3s"])
plt.yticks([50, 150], ["0s", "3s"])
plt.ylim([200, 0])
plt.title("Distance Matrix for Digits")
plt.colorbar()

# When you run this code, you'll see a plot of the images
# at their projected locations
plt.figure(figsize=(12, 12))
pca = PCA(n_components=2)
Y = pca.fit_transform(X)
imscatter(Y, X, 28)
plt.show()