import numpy as np

operator = 'and'

#----------------

atributes = np.array([ [0, 0], [0, 1], [1, 0], [1, 1]])

if operator == 'and':
	labels = np.array([0, 0, 0, 1])
elif operator == 'or':
	labels = np.array([0, 1, 1, 1])
elif operator == 'xor':
	labels = np.array([0, 1, 1, 0])

#----------------

w = [+9, +9] #initial random values for weights

threshold = 5
alpha = 0.5 #learning rate
epoch = 1000 #learning time
#----------------

print("learning rate: ", alpha,", threshold: ", threshold)

for i in range(0, epoch):
	print("epoch ", i+1)
	global_delta = 0 #this variable is used to terminate the for loop if learning completed in early epoch
	for j in range(len(atributes)):

		actual = labels[j]

		sum = atributes[j][0]*w[0] + atributes[j][1]*w[1]

		if sum > threshold: #then fire
			predicted = 1
		else: #do not fire
			predicted = 0

		delta = actual - predicted
		global_delta = global_delta + abs(delta)

		#update weights with respect to the error
		for k in range(0, 2):
			w[k] = w[k] + delta * alpha

		print(atributes[j][0]," ", operator, " ", atributes[j][1], " -> actual: ", actual, ", predicted: ", predicted, " (w: ",w[0],")")

	if global_delta == 0:
		break

	print("------------------------------")


"""
An implementation of the cheeger cut for networkx graphs.
"""
import numpy as np
import scipy as sp
import scipy.sparse
import scipy.sparse.linalg
import networkx as nx

def sweep_set(A, v_2, degrees):
    """
    Given the adjacency matrix of a graph, and the second eigenvalue of the laplacian matrix, use the sweep set
    algorithm to find a sparse cut.

    :param A: The adjacency matrix of the graph to use.
    :param v_2: The second eigenvector of the laplacian matrix of the graph
    :param degrees: a list with the degrees of each vertex in the graph
    :return: The set of vertices corresponding to the optimal cut
    """

    # Calculate n here once
    n = A.shape[0]
    # Keep track of the best cut so far
    best_cut_index = None
    best_conductance = None

    # Keep track of the size of the set and the cut weight to make computing the conductance
    # straightforward
    total_volume = np.sum(degrees)
    set_volume = 0
    set_size = 0
    cut_weight = 0

    # Normalise v_2 with the degrees of each vertex
    D = sp.sparse.diags(degrees, 0)
    v_2 = D.power(-(1/2)).dot(v_2)

    # First, sort the vertices based on their value in the second eigenvector
    sorted_vertices = [i for i, v in sorted(enumerate(v_2), key=(lambda x: x[1]))]

    # Keep track of which edges to add/subtract from the cut each time
    x = np.ones(n)

    # Loop through the vertices in the graph
    for (i, v) in enumerate(sorted_vertices[:-1]):
        # Update the set size and cut weight
        set_volume += degrees[v]
        set_size += 1

        # From now on, edges to this vertex will be removed from the cut at each iteration.
        x[v] = -1

        additional_weight = A[v, :].dot(x)
        cut_weight += additional_weight

        if cut_weight < 0:
            raise Exception('Something went wrong in sweep set: conducatance negative!')
        # Calculate the conductance
        if min(set_volume, total_volume - set_volume) == 0:
            this_conductance = 1
        else:
            this_conductance = cut_weight / min(set_volume, total_volume - set_volume)

        # Check whether this conductance is the best
        if best_conductance is None or this_conductance < best_conductance:
            best_cut_index = i
            best_conductance = this_conductance

    # Return the best cut
    return sorted_vertices[:best_cut_index+1]

def cheeger_cut(G):
    """
    Given a networkx graph G, find the cheeger cut.

    :param G: The graph on which to operate
    :return: A set containing the vertices on one side of the cheeger cut
    """
    if G.number_of_nodes() == 0:
        raise Exception(f'Cheeger cut: Graph should not be empty!')

    if nx.is_connected(G) is False:
        return list(list(nx.connected_components(G))[0])

    if G.number_of_nodes() == 1:
        return list(G.nodes())

    if G.number_of_nodes() == 2:
        return [list(G.nodes)[0]]

    # Compute the key graph matrices
    adjacency_matrix = nx.adjacency_matrix(G, weight='weight')
    laplacian_matrix = nx.normalized_laplacian_matrix(G, weight='weight')
    graph_degrees = [t[1] for t in nx.degree(G, weight='weight')]

    # Compute the second smallest eigenvalue of the laplacian matrix
    eig_vals, eig_vecs = sp.sparse.linalg.eigsh(laplacian_matrix, which="SM", k=2)
    v_2 = eig_vecs[:, 1]

    # Perform the sweep set operation to find the sparsest cut
    S = sweep_set(adjacency_matrix, v_2, graph_degrees)
    nodes = list(G.nodes())
    return [nodes[i] for i in S]

import numpy,import scipy,import networkx


ch#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Oct 10 16:15:03 2020

@author: qd
"""
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
plt.style.use('seaborn-pastel')

N_POINTS = 100
RAYON = .5
ANGLE = 0
SPEED = 10
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.axis('equal')
ax.set_xlim([-2, 2])
ax.set_ylim([-2, 2])
scatter = ax.scatter([], [], c='red', marker='+')

def points_on_circumference(x, y, r, precision=100):
    return [(np.cos(2*np.pi / precision*(i+x) )*r,
             np.sin(2*np.pi / precision*(i+y) )*r)
            for i in np.arange(0, precision+1)]

def current_point_on_circle(xy: tuple, t, r, speed=1,
                            precision=100):
    center_x, center_y = xy[0], xy[1]
    return (np.cos(2*np.pi/precision*(t*speed + center_x) )*r,
            np.sin(2*np.pi/precision*(t*speed + center_y) )*r)

points = points_on_circumference(0, 0, RAYON, N_POINTS)

def init():
    c = plt.Circle((0, 0), RAYON, color='blue', fill=False)
    c2 = plt.Circle(points[0], RAYON/2, color='blue', fill=False)
    ax.add_artist(c)
    ax.add_artist(c2)
    scatter.set_offsets(np.hstack(points[0]))
    return scatter, c, c2

def animate(i):
    x = [x[0] for x in points]
    y = [y[1] for y in points]
    scatter.set_offsets(np.hstack((x[i%N_POINTS], y[i%N_POINTS])))
    return scatter,

anim = FuncAnimation(fig, animate, init_func=init,
                     frames=200, interval=20, blit=True)

# anim.save('circle_around2.gif', writer='imagemagick')
"""
c = plt.Circle((0, 0), RAYON, color='blue', fill=False)
ax.add_artist(c)
x = [x[0] for x in points]
y = [y[1] for y in points]
scatter.set_offsets(np.hstack((x[0], y[0])))
fig.savefig('test_speed_1circle.png')
"""
"""
c = plt.Circle((0, 0), RAYON, color='blue', fill=False)
center_circle2 = current_point_on_circle((0, 0), 1, RAYON)
c2 = plt.Circle(center_circle2, RAYON/2, color='blue', fill=False)
ax.add_artist(c2)
ax.add_artist(c)
x = [x[0] for x in points]
y = [y[1] for y in points]
scatter.set_offsets(np.hstack((x[1*SPEED], y[1*SPEED])))
scatter.set_offsets(np.hstack(center_circle2))
fig.savefig('test_twocircles_t1.png')
"""


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

fig, (ax_gamma, ax_beta) = plt.subplots(ncols=1, nrows=2, figsize=(20,16))

x_gamma = np.linspace(0, 10, 1000)
y_gamma = stats.gamma.pdf(x_gamma, a = bgf.params_['alpha'], scale = bgf.params_['r'])
ax_gamma.plot(x_gamma, y_gamma,'-')
ax_gamma.set_title(f'Gamma distribution (alpha = {bgf.params_["alpha"]:.2f}, scale (r)  = {bgf.params_["r"]:.2f})')
ax_gamma.set_xlabel(r'$\lambda$')
ax_gamma.set_ylabel(r'$P(\lambda)$')

x_beta = np.linspace(0, 1, 100)
y_beta = stats.beta.pdf(x_beta, a=bgf.params_["a"], b=bgf.params_["b"])
ax_beta.plot(x_beta, y_beta, "-")
ax_beta.set_title(f'Beta distribution (a = {bgf.params_["a"]:.2f}, b  = {bgf.params_["b"]:.2f})')
ax_beta.set_xlabel('p')
ax_beta.set_ylabel('P(p)')
plt.show()

# Frequency/Recency/Future Purchases Matrix
from lifetimes.plotting import plot_frequency_recency_matrix

_ = plot_frequency_recency_matrix(bgf)

# Frequency/Recency/Probability Alive Matrix
from lifetimes.plotting import plot_probability_alive_matrix

_ = plot_probability_alive_matrix(bgf)

# CLV estimation using BG-NBD
# The predicted number of transactions in the next 10 weeks
rfm_cal_holdout['n_transactions_10_pred'] = bgf.predict(t=10,
                                                        frequency=rfm_cal_holdout['frequency_holdout'],
                                                        recency=rfm_cal_holdout['recency_cal'],
                                                        T=rfm_cal_holdout['T_cal'])

# The probability of being alive
rfm_cal_holdout['alive_prob'] = bgf.conditional_probability_alive(frequency = rfm_cal_holdout['frequency_cal'],
                                                                  recency = rfm_cal_holdout['recency_cal'],
                                                                  T = rfm_cal_holdout['T_cal'])

# Multiply the alive probability and number of purchases and average past purchase
rfm_cal_holdout['value_10_pred'] = rfm_cal_holdout['alive_prob'] * \
                                  rfm_cal_holdout['n_transactions_10_pred'] *\
                                rfm_cal_holdout['monetary_value_cal']

rfm_cal_holdout[["value_10_pred", "alive_prob", "n_transactions_10_pred", "monetary_value_cal"]].head()

rfm_cal_holdout['value_10_pred'].describe()

# Plot a histogram
import seaborn as sns

fig, ax = plt.subplots(figsize=(12, 7))
ax = sns.histplot(rfm_cal_holdout['value_10_pred'],
                  kde=False,
                  binwidth=2)
ax.set_title(f'Customer value histogram')
ax.set_xlabel(r'Customer value estimate for 10 periods ($)')
ax.set_ylabel(r'Number of customers in each bin')
ax.set_xlim(-2,20)

plt.show()

# Proactive intervention
VALUE_10_PRED_THRESHOLD = 10

# Filter for high-value customers
rfm_cal_holdout.loc[rfm_cal_holdout['value_10_pred'] > VALUE_10_PRED_THRESHOLD, ['value_10_pred']]


#!/usr/bin/env python

import numpy as np
from tqdm import tqdm

# we have only one neuron
NEURONS = 1

# learning rate
LEARNING_RATE = 0.01

# number of iterations
NUM_OF_ITERS = 100000

# small difference to be able to approximately calculcate derivative
EPSILON = 0.01

class TwoVariablesBooleanFunctionNeuralNetwork:
    def __init__(self, x, y):
        #  - n rows: the number of samples
        self.n = x.shape[0]

        #  - m columns: the number of input variables
        self.m = x.shape[1]

        # x is a np.array which is a n x m matrix. It has:
        #  - n rows: the number of samples
        #  - m columns: the number of input variables
        self.input = x

        # np.shape returns a tuple: (n,m)
        # W is a matrix of m x l:
        #  - m rows: the number of input variables
        #  - l columns: the number of neurons
        self.W = np.random.rand(self.m, NEURONS)

        # y and output are column vectors of size n
        self.y      = y
        self.output = np.zeros(self.y.shape)

    def feedforward(self):
        self.output = sigmoid(np.dot(self.input, self.W))

    def J(self, W):
        # here the np.dot multiplication will give back
        # a column vector, which we reshape (so that it will
        # be transposed to a row vector), and then squeeze (to
        # get rid of the extra dimension)
        g_xw = np.array(sigmoid(np.dot(self.input, W))).reshape(1,-1).squeeze()

        # calculate the difference between our hypothesis and
        # the desired values
        err_diff = self.y - g_xw

        # return the error as sum of squared difference
        return (1 / self.n) * np.sum(np.square(err_diff))

    def W_i_plus(self, i):
        W_copy = self.W.copy()
        W_copy[i][0] = W_copy[i][0] + EPSILON
        return W_copy

    def W_i_minus(self, i):
        W_copy = self.W.copy()
        W_copy[i][0] = W_copy[i][0] - EPSILON
        return W_copy

    def delta_J_i(self, i):
        return (
            (self.J(self.W_i_plus(i)) - self.J(self.W_i_minus(i))) / (2*EPSILON)
        )

    def backprop(self):
        delta_J = []

        for i in range(0,self.m):
            delta_J += [self.delta_J_i(i)]

        delta_J = np.array(delta_J).reshape(-1,1)

        self.W -= LEARNING_RATE * delta_J

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

if __name__ == "__main__":
    x = np.array([[1,0,0],
                  [1,0,1],
                  [1,1,0],
                  [1,1,1]])

    y = np.array([0,0,0,1])

    nn = TwoVariablesBooleanFunctionNeuralNetwork(x,y)

    pbar = tqdm(total = NUM_OF_ITERS)

    for i in range(0, NUM_OF_ITERS):
        nn.feedforward()
        nn.backprop()
        pbar.update(1)

    print("Number of iterations: {}".format(NUM_OF_ITERS))
    print("Weights (W0, W1, ... Wm):\n{}".format(nn.W))
    print("Output (h_W(x)_0, h_W(x)_1, ... h_W(x)_n):\n{}".format(nn.output))


import numpy as np
import math
from scipy.interpolate import BSpline

def gen_BSplines(n, size_step, deg, shift=0):
    """
    Generates Bsplines on [1,n] where the knots are spaces by size_step and the polynomials are degree deg. Results are stored in a matrix D. Support of atoms has size (1+deg)*size_step. By default the grid for knots is [0,size_step, 2*size_step...] but it can be shifted.

    Parameters
    ----------
    n : int
        The splines are valued on [1,n]

    size-step : int
        The number of indices in [1,n] between each knot

    deg : int
        degree of the splines

    shift : int (default 0)
        Set a shift for the origin of the splines

    Returns
    -------
    D : numpy array
        A matrix containing the B-splines columnwise
    """

    n_over_m = size_step
    m = int(n/n_over_m + 2*(deg-1)+1) # adding left and right padding
    t = shift+np.linspace(-(deg-1)*n_over_m,n+(deg-1)*n_over_m,m)
    mySplines = BSpline(t,np.ones(n),deg)

    # todo: start before 0 to have nontrivial elements on the borders
    D = []
    for i in range(0,m-deg):
        Bspline = mySplines.basis_element(t[i:i+deg], extrapolate=False)
        D.append(Bspline(np.linspace(1,n,n)))
    D = np.array(D).T
    D[np.isnan(D)]=0
    return D

def sam(x,y):
    """
    Computes the Spectral Angular Mapper between two spectra x and y.

    Parameters
    ----------
    x : numpy array
        a spectra to compare with y

    y : numpy array
        a spectra to compare with x

    Returns
    -------
    out : float
        the SAM between x and y
    """

    return math.acos(np.dot(x,y)/np.linalg.norm(x)/np.linalg.norm(y))


## @ingroup Methods-Aerodynamics-Common-Gas_Dynamics
# oblique_shock.py
#
# Created:  Jan 2018, W. Maier
# Modified:
#

# ----------------------------------------------------------------------
#  Imports
# ----------------------------------------------------------------------

import numpy as np

# ----------------------------------------------------------------------
#  Oblique Shock Relations
# ----------------------------------------------------------------------

## @ingroup Methods-Aerodynamics-Common-Gas_Dynamics
def oblique_shock_relations(M0,gamma,theta,beta):
    """Computes flow quatities/ratios after undergoing
    an oblique shock

    Assumptions:
    None

    Source:
    Chapter 12 of:
    https://web.stanford.edu/~cantwell/AA210A_Course_Material/AA210A_Course_Notes/

    https://arc.aiaa.org/doi/pdf/10.2514/2.2349

    Inputs:
    Mach, M0                             [-]
    Isentropic Expansion Factor, gamma   [-]
    Flow turn angle, theta               [rad]
    Shock Angle, beta                    [rad]

    Outputs:
    Mach, M1                             [-]
    Static Temperature Ratio, Tr         [-]
    Static Pressure Ratio, Pr            [-]
    Stagnation Pressure Ratio, Ptr       [-]

    Properties Used:
    N/A
    """

    # determine normal component of machs
    M0_n = M0*np.sin(beta)
    M1_n = np.sqrt(((gamma-1.)*M0_n*M0_n+2.)/(2.*gamma*M0_n*M0_n-(gamma-1.)))

    # determine flow quantaties and ratios
    M1   = M1_n/np.sin(beta-theta)
    Pr   = (2.*gamma*M0_n*M0_n-(gamma-1.))/(gamma+1.)
    Tr   = Pr*((((gamma-1.)*M0_n*M0_n)+2.)/((gamma+1.)*M0_n*M0_n))
    Ptr  = ((((gamma+1.)*M0_n*M0_n)/((gamma-1.)*M0_n*M0_n+2))**(gamma/(gamma-1.)))*((gamma+1.)/(2.*gamma*M0_n*M0_n-(gamma-1.)))**(1./(gamma-1.))

    return M1,Pr,Tr,Ptr

def theta_beta_mach(M0,gamma,theta,n=0):
    """Computes shock angle of an oblique shock

        Assumptions:
        None

        Source:
        Chapter 12 of:
        https://web.stanford.edu/~cantwell/AA210A_Course_Material/AA210A_Course_Notes/

        Inputs:
        Mach, M0                             [-]
        Isentropic Expansion Factor, gamma   [-]
        Flow turn angle, theta               [rad]
        Strong Shock (0 = weak), delta       [-]

        Outputs:
        Shock Angle, Beta                    [rad]

        Properties Used:
        N/A
        """

    # Calculate wave angle
    mu   = np.arcsin(1./M0)
    c    = np.tan(mu)*np.tan(mu)

    # Calculate shock angle
    a    = ((gamma-1.)/2.+(gamma+1.)*c/2.)*np.tan(theta)
    b    = ((gamma+1.)/2.+(gamma+3.)*c/2.)*np.tan(theta)
    d    = np.sqrt(((4.*(1.-3.*a*b)**3.)/(27.*a*a*c+9.*a*b-2.)**2)-1.)

    beta =np.arctan((b+9.*a*c)/(2.*(1.-3.*a*b))-(d*(27.*a**2.*c+9.*a*b-2))/(6.*a*(1.-3.*a*b))*np.tan(n*np.pi/3.+1./3.*np.arctan(1./d)))

    return beta


#!/usr/bin/env python

from sympy import pi, cos, sin, sqrt, arg, exp, Abs, S
from sympy import integrate, Rational, nfloat, conjugate, Symbol
from sympy.functions.special.delta_functions import Piecewise

t = Symbol('t')

def Heaviside(arg):
    ''' Heaviside die ook op arg=0 gedefinieerd is '''
    return Piecewise((0, arg < 0), (1, arg >= 0))

# functies om de fourier-reeks coefficienten uit te rekenen
def an(f, T, n, omega0):
    return 2/T*integrate(f*cos(n*omega0*t), (t, 0, T))

def bn(f, T, n, omega0):
    return 2/T*integrate(f*sin(n*omega0*t), (t, 0, T))

def a0(f, T, omega0):
    return 1/T*integrate(f, (t, 0, T)).nsimplify()

def fourreeks(f, T, N):
    N += 1  # voor Maple
    a = (N-1)*[0]
    b = (N-1)*[0]
    omega0 = 2*pi/T
    som = a0(f, T, omega0)
    for n in range(1, N):
        a[n-1] = an(f, T, n, omega0)
        b[n-1] = bn(f, T, n, omega0)
        som += (a[n-1]*cos(n*omega0*t) + b[n-1]*sin(n*omega0*t)).nsimplify()
    return som, a, b

def spec(a, b, T):
    omega0 = 2*pi/T
    mag = [nfloat(sqrt(a[i]**2 + b[i]**2)) for i in range(len(a))]
    phi = len(a)*[0]
    for i in range(len(b)):
        if a[i] == S.Zero:
            phi[i] = 0
        else:
            phi[i] = nfloat(arg(a[i] - S.ImaginaryUnit*b[i]))
    mag.insert(0, a0(f, T, omega0))
    if mag[0] >= 0:
        phi.insert(0, 0)
    else:
        phi.insert(0, pi)
    return (mag, phi)

def periodiek(f, T, N):
    som = S(0)
    for n in range(N):
        som += (Heaviside(t-n*T) - Heaviside(t-(n+1)*T))*f.replace(t, t-n*T)
    return som

def plot_color(p):
    color = ['r', 'b', 'g', 'y', 'm']
    Nc = len(color)
    for i, pc in enumerate(p):
        pc.line_color = color[i % Nc]

if __name__ == '__main__':
    from sympy.plotting import plot
    import matplotlib.pyplot as plt

    f = Heaviside(t+1) - Heaviside(t-1)
    T = 4
    N = 5
    teind = 10
    som, a, b = fourreeks(f, T, N)
    mag, phase = spec(a, b, T)
    plot(som, (t, 0, teind))
    p = plot(periodiek(f, T, int(teind/T)+1), som, (t, 0, teind),
             adaptive=False, show=False)
    plot_color(p)
    p.show()
    phase, mag = spec(a, b, T)
    plt.figure()
    plt.subplot(2, 1, 1)
    plt.plot(range(len(phase)), phase)
    plt.subplot(2, 1, 2)
    plt.plot(range(len(mag)), mag)
    plt.show()


import math

from helpers.executor import Executor

from helpers.util import *
import itertools
from itertools import *
import re
from re import *
import numpy as np

from typing import Any, Callable, Generator, Sequence

day, year = 7, 2021
split_seq = ','

class Solution(Executor):
    def solve(self, r: Sequence[str], print: Callable[..., None]) -> Generator[Any, None, None]:
        yield self._solve_part1(r, print)
        yield self._solve_part2(r, print)

    def _solve_part1(self, r: Sequence[str], print: Callable[..., None]) -> Any:
        s = np.array(as_type(r, int))

        min = (None, math.inf)
        for x in range(0, max(s)):
            if np.sum(np.abs(s - x)) < min[1]:
                min = (x, np.sum(np.abs(s - x)))
        return min[1]

    def _solve_part2(self, r: Sequence[str], print: Callable[..., None]) -> Any:
        s = np.array(as_type(r, int))

        min = (None, math.inf)
        for x in range(0, max(s)):
            d = np.abs(s - x)
            if np.sum(d * (d + 1) / 2) < min[1]:
                min = (x, np.sum(d * (d + 1) / 2))
        return min

if __name__ == '__main__':
    solution = Solution(year, day)
    solution.execute(split_seq, use_cached_test_cases=True)


""" @file controls.py
"""

from scipy.linalg import solve_continuous_are as care
import numpy as np
import copy

###############################
## Control Policies
###############################
class ZeroPol:

    def __init__(self, du):
        self.du = du
        self.u = np.zeros((du))

    def evaluate(self, time, state):
        return self.u

class LinearFeedback: # Continuous Infinite-Horizon Linear Quadratic Regulator

    def __init__(self, A, B, Q, R):
        self.P = care(A, B, Q, R)
        self.K = np.linalg.solve(np.linalg.inv(R), np.dot(B.T, self.P))
        self.A = A
        self.B = B

        self.R = R
        self.Q = Q

    def evaluate(self, time, state):
        return -np.dot(self.K, state)

    def get_P(self):
        return self.P

    def get_Q(self):
        return self.Q

    def get_R(self):
        return self.R

class LinearFeedbackConstTracker:

    """ Class for linear quadratic tracker for constant state tracking
    """

    def __init__(self, A, B, Q, R, const, g=None):

        """ LinearFeedbackConstTracker constructor

        Input:
        - A:            linear time-invariant state matrix
        - B:            linear time-invariant input matrix
        - Q:            control weighting matrix for state
        - R:            control weighting matrix for control inputs
        - const:        constant state to track
        - g:            state to track in error dynamics system

        """

        self.A = A
        self.B = B
        self.Q = Q
        self.R = R

        self.P = care(A, B, Q, R)
        self.K = np.linalg.solve(R, np.dot(B.T, self.P))
        self.Bt = copy.deepcopy(B.T)
        self.RBt = np.dot(np.linalg.inv(R), self.Bt)
        self.BRBt = np.dot(B, self.RBt)
        if g is None:
            self.g = np.dot(A, const)
        else:
            self.g = copy.deepcopy(g)
        # print("g = ", self.g)

        self.p = -np.linalg.solve(A.T - np.dot(self.P, self.BRBt), np.dot(self.P, self.g))
        self.R = copy.deepcopy(R)
        self.const = copy.deepcopy(const)

        # Closed loop is
        # \dot{x} = A_cl x + g_cl
        self.Acl = A - np.dot(B, self.K)

        self.g_cl = np.dot(B, np.dot(self.K, const)) - np.dot(B, np.dot(np.linalg.inv(R), np.dot(B.T, self.p)))

        self.tracking = None

        # steady-state
        self.xss = np.dot(self.BRBt, self.p) - self.g
        self.xss = np.dot(np.linalg.inv(self.A - np.dot(self.BRBt.T, self.P)), self.xss)
        # self.xss = self.xss - self.const

        # steady-state optimal control
        # self.uss = self.evaluate(0, self.xss)
        self.uss = -np.dot(self.RBt, np.dot(self.P, self.xss) + self.p)

    def evaluate(self, time, state):
        # print("TIME: ", time, " STATE: ", state.T)

        return -np.dot(self.RBt, np.dot(self.P, state - self.const) + self.p)

    def get_closed_loop_A(self):
        return self.Acl

    def get_closed_loop_g(self):
        return self.g_cl

    def get_P(self):
        return self.P

    def get_Q(self):
        return self.Q

    def get_R(self):
        return self.R

    def get_p(self):
        return self.p

    def get_uss(self):
        return self.uss
        # return -np.dot(self.RBt, np.dot(self.P, self.xss) + self.p)

    def get_xss(self):
        return self.xss

    def set_const(self, time, target_id, const):
        self.tracking = target_id

        self.g = np.dot(self.A, const)

        self.p = -np.linalg.solve(self.A.T - np.dot(self.P, self.BRBt), np.dot(self.P, self.g))
        self.const = copy.deepcopy(const)

        # Closed loop is
        # \dot{x} = A_cl x + g_cl
        self.Acl = self.A - np.dot(self.B, self.K)

        self.g_cl = np.dot(self.B, np.dot(self.K, self.const)) - np.dot(self.B,
                np.dot(np.linalg.inv(self.R), np.dot(self.B.T, self.p)))

        self.tracking = None

        # steady-state
        self.xss = np.dot(self.BRBt, self.p) - self.g
        self.xss = np.dot(np.linalg.inv(self.A - np.dot(self.BRBt.T, self.P)), self.xss)

        self.uss = -np.dot(self.RBt, np.dot(self.P, self.xss) + self.p)

    def cost_to_go(self, time, state1, const_state):

        """ Computes cost_to_go for a agent/target to track a single agent/target
        """

        error_state = state1 - const_state

        g = np.dot(self.A, const_state)

        p = -np.linalg.solve(self.A.T - np.dot(self.P, self.BRBt), np.dot(self.P, g))

        g_cl = np.dot(self.B, np.dot(self.K, const_state)) - np.dot(self.B, np.dot(np.linalg.inv(self.R),
            np.dot(self.B.T, p)))

        self.tracking = None

        # steady-state
        xss = np.dot(self.BRBt, p) - g
        xss = np.dot(np.linalg.inv(self.A - np.dot(self.BRBt.T, self.P)), xss)

        # steady-state optimal control
        # self.uss = self.evaluate(0, self.xss)
        uss = -np.dot(self.RBt, np.dot(self.P, self.xss) + self.p)

        cost_to_go = np.dot(error_state, np.dot(self.P, error_state)) + 2*np.dot(p.T, error_state) -\
            np.dot(xss, np.dot(self.P, xss)) + 2*np.dot(p.T, xss)

        return cost_to_go

# IN DEVELOPMENT
class MinimumTimeIntercept(): # augmented proportional navigation - min time intercept with target accel

    """
    see bryson and ho (1975) 288 ch.9 eqn. 9.4.30
    """

    def __init__(self, time_final, dx):
        self.time_final = time_final
        self.dim_position = dx
        self.cp = 1
        self.ce = 3

    def get_final_time(self, state2):
        pass

    def track(self):
        pass

    def evaluate(self, time, state1, state2, feedforward=0):

        t = time
        tf = self.time_final

        ce = self.ce
        cp = self.cp

        # pursuer position
        rp = state1[:3]
        # pursuer velocity
        vp = state1[3:6]

        # evader position
        re = state2[:3]
        # evader velocity
        ve = state2[3:6]

        # proportional navigation guidance law
        u = -3/((1-ce/cp)*(tf-t)**2) * (rp-re + (vp-ve)*(tf-t) )

        return u

    def cost_to_go(self, time, state1, state2):
        # y1 = state1[:self.dim_position] - state2[:self.dim_position] # relative position
        # y2 = state1[self.dim_position:] - state2[self.dim_position:] # relative velocity
        # return  y1 + y2*(time_final-time) # miss distance
        time_to_go = self.time_final - time


'''
learn a little bit about markov chains

'''

import numpy as np
import argparse

mat = np.array([ # state transition matrix
    [2,6,2],
    [1,6,3],
    [2,7,1]
])/10
states = "sleep,run,icecream".split(',')

class MarkovSimple:
    def __init__(self,transitionmatrix:np.ndarray,statenames:list,initialstate=-1):
        ''' initialize markov chain. if no initial state given, one chosen at random'''
        self.m = transitionmatrix
        self.s = statenames
        r,c = self.m.shape
        assert r == c, "state transition matrix isn't square"
        assert r == len(self.s), "number of states doesn't match matrix"
        irow:np.ndarray = None
        for irow in self.m:
            assert np.isclose(irow.sum(),1.0),"row doesn't add up to 1: {}".format(irow)
        if(initialstate == -1):
            initialstate = np.random.choice(len(self.s))
        self.curr_state = initialstate
        self.hist = [self.curr_state]
        print('initial state:',self.s[self.curr_state])

    def next(self):
        ''' based on current state, go to next state'''
        irow = self.m[self.curr_state]
        self.curr_state = np.random.choice(len(self.s),p=irow)
        self.hist.append(self.curr_state)
        print('new state:',self.s[self.curr_state])

if(__name__ == '__main__'):
    # test out weather markov chain, using probabilities (not dice)
    mat = np.array([
        [0.7,0,0,0,0.09,0.075,0.09,0.045],
        [0,0.7,0,0,0.09,0.075,0.09,0.045],
        [0,0,0.7,0,0.09,0.075,0.09,0.045],
        [0,0,0,0.7,0.09,0.075,0.09,0.045],
        [0.075,0.075,0.075,0.075,0.21,0.175,0.21,0.105],
        [0.075,0.075,0.075,0.075,0.21,0.175,0.21,0.105],
        [0.075,0.075,0.075,0.075,0.21,0.175,0.21,0.105],
        [0.075,0.075,0.075,0.075,0.21,0.175,0.21,0.105],
    ])
    states = 'cool & foggy,clear & cool,clear & warm,cloudy & warm,clear & dry,rain showers,rain,storm'.split(',')
    am = MarkovSimple(mat, states)
    for i in range(10):
        am.next()

# eof


# -*- coding: utf-8 -*-
"""
Created on Sun Dec 24 21:32:08 2017

@author: chris
"""

import os
os.chdir("E:\\workspace\\project")

from mymodule.GMM import GMM
import numpy as np
from scipy import stats
from matplotlib import pyplot as plt

def mixture(x,p,mu,sigma):
    mix = p[0]*stats.multivariate_normal.pdf(x,mu[0],sigma[0])
    for i in range(1,len(p)):
        mix += p[i]*stats.multivariate_normal.pdf(x,mu[i],sigma[i])
    return mix

def scatter_contour(y,model,G,devide=True):
    right=0
    colors = ['tab:blue','tab:orange','tab:olive']
    fig = plt.figure()
    # scatter
    for i in range(G):
        left = right
        right += group[i]
        plt.scatter(y[left:right,0],y[left:right,1],alpha=0.6,color=colors[i])
    # contour
    if (devide):
        for i in range(model.g):
            A,B = np.meshgrid(np.arange(min(y[:,0]),max(y[:,0]),0.1),np.arange(min(y[:,1]),max(y[:,1]),0.1))
            tmp = np.append(A.reshape(A.shape[0]*A.shape[1],1),B.reshape(B.shape[0]*B.shape[1],1),1)
            C = stats.multivariate_normal.pdf(tmp,model.mu[i],model.sigma[i]).reshape(A.shape)
            plt.contour(A,B,C,5)
    else:
        A,B = np.meshgrid(np.arange(min(Y[:,0]),max(Y[:,0]),0.1),np.arange(min(Y[:,1]),max(Y[:,1]),0.1))
        tmp = np.append(A.reshape(A.shape[0]*A.shape[1],1),B.reshape(B.shape[0]*B.shape[1],1),1)
        C = mixture(tmp,model.p,model.mu,model.sigma).reshape(A.shape)
        plt.contour(A,B,C,10)
    return fig

G = 3
N = 1000
P = [0.5,
     0.3,
     0.2]
MU = [[-3,0],
      [0,0],
      [0,3]]

# Homo
SIGMA = [[[1,0.2],[0.2,1]],
         [[1,0.2],[0.2,1]],
         [[1,0.2],[0.2,1]]]

# single trial
np.random.seed(0)
group = np.random.multinomial(N,P)
Y = np.random.multivariate_normal(MU[0],SIGMA[0],group[0])
for i in range(1,len(P)):
    Y = np.append(Y,np.random.multivariate_normal(MU[i],SIGMA[i],group[i]),axis=0)

# train model
model = GMM(Y,G,True,1e-3)
# plot hetero_model_homo_data.png
fig = scatter_contour(Y,model,G,True)
fig.savefig("hetero_model_homo_data.png",dpi=120)
# plot hetero_model_homo_data_mixcontour.png
fig = scatter_contour(Y,model,G,False)
fig.savefig("hetero_model_homo_data_mixcontour.png",dpi=120)

# train model
model = GMM(Y,G,False,1e-3)
# plot homo_model_homo_data.png
fig = scatter_contour(Y,model,G,True)
fig.savefig("homo_model_homo_data.png",dpi=120)
# plot homo_model_homo_data_mixcontour.png
fig = scatter_contour(Y,model,G,False)
fig.savefig("homo_model_homo_data_mixcontour.png",dpi=120)


import numpy as np
import scipy
import scipy.stats as ss

def calc_2d_pdf(data, pdf_res = 0.3, minval = -10, maxval = 10):

    #x_bins = np.linspace(np.amin(data[0,0:]), np.amax(data[0,0:]), pdf_res)
    #y_bins = np.linspace(np.amin(data[1,0:]), np.amax(data[1,0:]), pdf_res)
    x_bins = np.arange(minval,maxval,pdf_res)
    y_bins = np.arange(minval,maxval,pdf_res)

    a,b,c = np.histogram2d(data[0,0:],data[1,0:],(x_bins,y_bins), density=True)

    return a

def integrate_2d(twod_data, minval = -10, maxval = 10, pdf_res = 0.3):

    xax_vals = np.arange(minval,maxval,pdf_res)[0:-1] + pdf_res/2
    temp = [np.trapz(y = twod_data[0:,i], x = xax_vals) for i in range(pdf1.shape[1])]
    int_val = np.trapz(y = temp, x = xax_vals)
    return int_val

#---------------------------------------------------------------------
#------------------------KL divergence module-------------------------
#---------------------------------------------------------------------

def calc_KL_divergence_2d(dat1, dat2):
    # assuming pdf1 is the 'true' pdf and pdf2 is sampled from it

    pdf1 = dat1.copy()#/np.sum(dat1)
    pdf2 = dat2.copy()#/np.sum(dat2)
    pdf1[pdf1 == 0] = 1e-10
    pdf2[pdf2 == 0] = 1e-10

    KLD = np.zeros_like(pdf1)
    for i in range(pdf1.shape[0]):
        for j in range(pdf1.shape[1]):

            KLD[i,j] = pdf1[i,j] * (np.log10(pdf1[i,j]/pdf2[i,j]))
#             print(pdf1[i,j], pdf2[i,j], (np.log10(pdf1[i,j]) - np.log10(pdf2[i,j])))
    return KLD

#---------------------------------------------------------------------
#------------------------KS test (2d) module--------------------------
#---------------------------------------------------------------------

def fhCounts(x,edge):
    # computes local CDF at a given point considering all possible axis orderings

    templist = [np.sum((x[0,0:] >= edge[0]) & (x[1,0:] >= edge[1])),
                np.sum((x[0,0:] <= edge[0]) & (x[1,0:] >= edge[1])),
                np.sum((x[0,0:] <= edge[0]) & (x[1,0:] <= edge[1])),
                np.sum((x[0,0:] >= edge[0]) & (x[1,0:] <= edge[1]))]
    return templist

def kstest_2d(dist1, dist2, alpha = 0.05):

    num1 = dist1.shape[1]
    num2 = dist2.shape[1]

    KSstat = -np.inf

    for iX in (np.arange(0,num1+num2)):

        if iX < num1:
            edge = dist1[0:,iX]
        else:
            edge = dist2[0:,iX-num1]

#         vfCDF1 = np.sum(fhCounts(dist1, edge)) / num1
#         vfCDF2 = np.sum(fhCounts(dist2, edge)) / num2

        vfCDF1 = np.array(fhCounts(dist1, edge)) / num1
        vfCDF2 = np.array(fhCounts(dist2, edge)) / num2

        vfThisKSTS = np.abs(vfCDF1 - vfCDF2)
        fKSTS = np.amax(vfThisKSTS)

        if (fKSTS > KSstat):
            KSstat = fKSTS
            #print(KSstat, vfCDF1, vfCDF2)

    # Peacock Z calculation and P estimation

    n =  num1 * num2 /(num1 + num2)
    Zn = np.sqrt(n) * KSstat;
    Zinf = Zn / (1 - 0.53 * n**(-0.9));
    pValue = 2 *np.exp(-2 * (Zinf - 0.5)**2);

    # Clip invalid values for P
    if pValue > 1.0:
        pValue = 1.0

#     H = (pValue <= alpha)

    return pValue, KSstat

#---------------------------------------------------------------------
#------------------------Hotelling T2 test module---------------------
#---------------------------------------------------------------------

def hotelling_T2(dist1, dist2):

    # Hotelling's T-Squared test for comparing d-dimensional data from two independent samples,
    # assuming normality w/ common covariance matrix

    if dist1.shape[0] != dist2.shape[0]:
        print('Error: ordered pair dimensions do not match')
    else:
        p = dist1.shape[0]

    n = dist1.shape[1]
    m = dist2.shape[1]

    n = n+m
    mux = np.mean(dist1,1)
    muy = np.mean(dist2,1)

    Sx = np.cov(dist1)
    Sy = np.cov(dist2)

    Su = (n*Sx + m*Sy) / (n-2) # unbiased estimate
    d = np.zeros((p,1))
    d[0:,0] = mux - muy

    D2 = np.matmul(d.T,np.matmul(np.linalg.inv(Su),d)) # reversing the formula for python's matrix handling
    #D2 = d.T*np.linalg.inv(Su)*d
    D2 = D2[0][0]
    T2 = ((n*m)/n)*D2
    F = T2 * (n-p-1) / ((n-2)*p);

    pval = 1 - scipy.stats.f.cdf(F,p,n-p-1);
    return pval, T2

#---------------------------------------------------------------------
#------------------------Hotelling T2 test module---------------------
#---------------------------------------------------------------------

def energy_statistics_test(dist1, dist2, alpha = 0.05, flag = 'szekely-rizzo', nboot = 100, replace = False, progbar = False):

    if dist1.shape[0] != dist2.shape[0]:
        print('Error: ordered pair dimensions do not match')

    n = dist1.shape[1]
    m = dist2.shape[1]

    pooled = np.hstack((dist1,dist2))

    e_n = energy(dist1, dist2, flag)
    e_n_boot = np.zeros((nboot, ))
    e_n_boot[0] = e_n

    if progbar == True:

        for i in tqdm(range(1,nboot)):

            if replace == False:
                ind = np.random.choice(n+m, size=(n+m,))
            else:
                ind = np.random.permutation(np.arange(n+m))
            e_n_boot[i] = energy(pooled[0:,ind[0:n]],pooled[0:,ind[n:]], flag)
    else:
        for i in (range(1,nboot)):

            if replace == False:
                ind = np.random.choice(n+m, size=(n+m,))
            else:
                ind = np.random.permutation(np.arange(n+m))
            e_n_boot[i] = energy(pooled[0:,ind[0:n]],pooled[0:,ind[n:]], flag)

    p = np.sum(e_n_boot>=e_n)/nboot

    return p, e_n, e_n_boot

def dist(x,y):
    dx = scipy.spatial.distance.pdist(x,'euclidean')
    dy = scipy.spatial.distance.pdist(y,'euclidean')
    dxy = scipy.spatial.distance.cdist(x.T,y.T,'euclidean')
    return dx,dy,dxy

def energy(x,y,flag):
    n = x.shape[1]
    m = y.shape[1]
    dx, dy, dxy = dist(x,y)

    if flag == 'aslan-zech':
        temp = -np.log(dxy)
        z = (1/(n*(n-1)))*np.sum(-np.log(dx)) + (1/(m*(m-1)))*np.sum(-np.log(dy)) - (1/(n*m))*np.sum(temp[(temp>-np.inf) & (temp<np.inf)])
    elif flag == 'szekely-rizzo':
        z = (2/(n*m))*np.sum(dxy) - (1/(n**2))*np.sum(2*dx) - (1/(m**2))*np.sum(2*dy)
        z = ((n*m)/(n+m))*z
    else:
        z = np.nan
        print('did not understand method input')

    return z


import numpy as np
from random import shuffle

def softmax_loss_naive(W, X, y, reg):
  """
  Softmax loss function, naive implementation (with loops)

  Inputs have dimension D, there are C classes, and we operate on minibatches
  of N examples.

  Inputs:
  - W: A numpy array of shape (D, C) containing weights.
  - X: A numpy array of shape (N, D) containing a minibatch of data.
  - y: A numpy array of shape (N,) containing training labels; y[i] = c means
    that X[i] has label c, where 0 <= c < C.
  - reg: (float) regularization strength

  Returns a tuple of:
  - loss as single float
  - gradient with respect to weights W; an array of same shape as W
  """
  # Initialize the loss and gradient to zero.
  loss = 0.0
  dW = np.zeros_like(W)

  #############################################################################
  # TODO: Compute the softmax loss and its gradient using explicit loops.     #
  # Store the loss in loss and the gradient in dW. If you are not careful     #
  # here, it is easy to run into numeric instability. Don't forget the        #
  # regularization!                                                           #
  #############################################################################
  #  Good article on softmax gradient derivation: https://madalinabuzau.github.io/2016/11/29/gradient-descent-on-a-softmax-cross-entropy-cost-function.html
  m = X.shape[0]
  for i in range(m):
        x_i = X[i]
        y_i = y[i]
        score = x_i.dot(W)
        stable_score = score - np.max(score)
        exp_sum = 0
        target = 0
        for i, score in enumerate(stable_score):
            exp_score = np.exp(score)
            exp_sum += exp_score
            if i == y_i:
                target = exp_score
        for i, score in enumerate(stable_score):
            exp_score = np.exp(score)
            scale = exp_score / exp_sum
            if i == y_i:
                dW[:, i] += ((scale - 1) * x_i).T
            else:
                dW[:, i] += (scale * x_i).T
        final_score = target / exp_sum
        loss += -np.log(final_score)

  loss /= m
  loss += reg * np.sum(W * W)
  dW /= m
  dW += 2 * reg * W
  #############################################################################
  #                          END OF YOUR CODE                                 #
  #############################################################################

  return loss, dW

def softmax_loss_vectorized(W, X, y, reg):
  """
  Softmax loss function, vectorized version.

  Inputs and outputs are the same as softmax_loss_naive.
  """
  # Initialize the loss and gradient to zero.
  loss = 0.0
  dW = np.zeros_like(W)

  #############################################################################
  # TODO: Compute the softmax loss and its gradient using no explicit loops.  #
  # Store the loss in loss and the gradient in dW. If you are not careful     #
  # here, it is easy to run into numeric instability. Don't forget the        #
  # regularization!                                                           #
  #############################################################################
  m = X.shape[0]

  # Loss Calculation
  logits = X.dot(W)
  stable_logits = logits - np.max(logits, axis=1, keepdims=True)
  scores = np.exp(stable_logits) / np.sum(np.exp(stable_logits), axis=1, keepdims=True)
  loss = np.sum(-np.log(scores[np.arange(m), y]))
  loss /= m
  loss += reg * np.sum(W * W)

  # Gradient Calculation
  scores[np.arange(m), y] -= 1
  dW = X.T.dot(scores)
  dW /= m
  dW += 2 * reg * W
  #############################################################################
  #                          END OF YOUR CODE                                 #
  #############################################################################

  return loss, dW


import math

import numpy as np

class ZeroVectorError(Exception): pass

class Vector3:

    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z

    @classmethod
    def unity(cls):
        return cls(1,1,1)

    @property
    def A(self):
        return np.array((self.x, self.y, self.z))

    def __add__(self, other):
        return Vector3(
            self.x + other.x,
            self.y + other.y,
            self.z + other.z
        )

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y and self.z == other.z

    def __sub__(self, other):
        return self + -other

    def __mul__(self, s):
        return Vector3(self.x * s, self.y * s, self.z * s)

    def __rmul__(self, s):
        return self * s

    def __neg__(self):
        return Vector3(-self.x, -self.y, -self.z)

    def __repr__(self):
        return "Vector3([{} {} {}])".format(self.x, self.y, self.z)

    @classmethod
    def zero(cls):
        """returns Vector(0,0,0)"""
        return cls(0, 0, 0)

    def as_tuple(self):
        return self.x, self.y, self.z

    def length(self):
        """ returns the magnitude of this vector """
        return math.sqrt(self.x**2 + self.y**2 + self.z**2)

    def dot(self, other):
        return self.x * other.x + self.y * other.y + self.z * other.z

    def cross(self, other):
        """ cross product for left handed coordinate system """
        a = self
        b = other
        return -1 * Vector3(
            a.y * b.z - a.z * b.y,
            a.z * b.x - a.x * b.z,
            a.x * b.y - a.y * b.x
        )

    def angle_between(self, other):
        """ returns the angle between this vector and other """
        dot = self.normalize().dot(other.normalize())
        angle = math.acos(dot)  # / (self.length() * other.length()))
        return angle

    def normalize(self):
        """ returns a vector in the same direction with unit length
        In the degenerate case of a zero vector, raises"""

        length = self.length()
        if length == 0:
            raise ZeroVectorError("can't normalize 0 vector")
        return Vector3(self.x / length, self.y / length, self.z / length)

    def lerp(self, other, t):
        """ lerp between self and other
        when t=0, returns self
        when t=1 returns other
        """
        return self + t*(other - self)


"""
Solving the two-dimensional diffusion equation

Example acquired from https://scipython.com/book/chapter-7-matplotlib/examples/the-two-dimensional-diffusion-equation/
"""

import numpy as np
import matplotlib.pyplot as plt

class SolveDiffusion2D:
    def __init__(self):
        """
        Constructor of class SolveDiffusion2D
        """
        # plate size, mm
        self.w = None
        self.h = None

        # intervals in x-, y- directions, mm
        self.dx = None
        self.dy = None

        # Number of discrete mesh points in X and Y directions
        self.nx = None
        self.ny = None

        # Thermal diffusivity of steel, mm^2/s
        self.D = None

        # Initial cold temperature of square domain
        self.T_cold = None

        # Initial hot temperature of circular disc at the center
        self.T_hot = None

        # Timestep
        self.dt = None

    def initialize_domain(self, w=10., h=10., dx=0.1, dy=0.1):

        assert isinstance(w, float)
        assert isinstance(h, float)
        assert isinstance(dx, float)
        assert isinstance(dy, float)

        self.w = w
        self.h = h
        self.dx = dx
        self.dy = dy
        self.nx = int(w / dx)
        #self.nx = int(h / dx)
        self.ny = int(h / dy)

    def initialize_physical_parameters(self, d=4., T_cold=300., T_hot=700.):

        assert isinstance(d, float)
        assert isinstance(T_cold, float)
        assert isinstance(T_hot, float)

        self.D = d
        self.T_cold = T_cold
        self.T_hot = T_hot

        # Computing a stable time step
        dx2, dy2 = self.dx * self.dx, self.dy * self.dy
        self.dt = dx2 * dy2 / (2 * self.D * (dx2 + dy2))

        #self.dt = dx2 + dy2 / (2 * self.D * (dx2 + dy2))

        print("dt = {}".format(self.dt))

    def set_initial_condition(self):
        u = self.T_cold * np.ones((self.nx, self.ny))

        # Initial conditions - circle of radius r centred at (cx,cy) (mm)
        r, cx, cy = 2, 5, 5
        r2 = r ** 2
        for i in range(self.nx):
            for j in range(self.ny):
                p2 = (i * self.dx - cx) ** 2 + (j * self.dy - cy) ** 2
                if p2 < r2:
                #if p2 > r2:
                    u[i, j] = self.T_hot

        return u.copy()

    def do_timestep(self, u_nm1):
        u = u_nm1.copy()

        dx2 = self.dx * self.dx
        dy2 = self.dy * self.dy

        # Propagate with forward-difference in time, central-difference in space
        u[1:-1, 1:-1] = u_nm1[1:-1, 1:-1] + self.D * self.dt * (
                (u_nm1[2:, 1:-1] - 2 * u_nm1[1:-1, 1:-1] + u_nm1[:-2, 1:-1]) / dx2
                + (u_nm1[1:-1, 2:] - 2 * u_nm1[1:-1, 1:-1] + u_nm1[1:-1, :-2]) / dy2)

        return u.copy()

    def create_figure(self, fig, u, n, fignum):
        fignum += 1
        ax = fig.add_subplot(220 + fignum)
        im = ax.imshow(u.copy(), cmap=plt.get_cmap('hot'), vmin=self.T_cold, vmax=self.T_hot)
        ax.set_axis_off()
        ax.set_title('{:.1f} ms'.format(n * self.dt * 1000))

        return fignum, im

def output_figure(fig, im):
    fig.subplots_adjust(right=0.85)
    cbar_ax = fig.add_axes([0.9, 0.15, 0.03, 0.7])
    cbar_ax.set_xlabel('$T$ / K', labelpad=20)
    fig.colorbar(im, cax=cbar_ax)
    plt.show()

def main():
    DiffusionSolver = SolveDiffusion2D()

    DiffusionSolver.initialize_domain()

    DiffusionSolver.initialize_physical_parameters()

    u0 = DiffusionSolver.set_initial_condition()
    print(u0)

    # Number of timesteps
    nsteps = 101

    # Output 4 figures at these timesteps
    n_output = [0, 10, 50, 100]

    fig_counter = 0
    fig = plt.figure()

    im = None

    # Time loop
    for n in range(nsteps):
        u = DiffusionSolver.do_timestep(u0)

        # Create figure
        if n in n_output:
            fig_counter, im = DiffusionSolver.create_figure(fig, u, n, fig_counter)

        u0 = u.copy()

    # Plot output figures
    output_figure(fig, im)

if __name__ == "__main__":
    main()


'''Functions for drawing contours of Dirichlet distributions.'''

# Author: Thomas Boggs

from __future__ import division, print_function

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

_corners = np.array([[0, 0], [1, 0], [0.5, 0.75**0.5]])
_AREA = 0.5 * 1 * 0.75**0.5
_triangle = tri.Triangulation(_corners[:, 0], _corners[:, 1])

# For each corner of the triangle, the pair of other corners
_pairs = [_corners[np.roll(range(3), -i)[1:]] for i in range(3)]
# The area of the triangle formed by point xy and another pair or points
tri_area = lambda xy, pair: 0.5 * np.linalg.norm(np.cross(*(pair - xy)))

def xy2bc(xy, tol=1.e-4):
    '''Converts 2D Cartesian coordinates to barycentric.
    Arguments:
        `xy`: A length-2 sequence containing the x and y value.
    '''
    coords = np.array([tri_area(xy, p) for p in _pairs]) / _AREA
    return np.clip(coords, tol, 1.0 - tol)

class Dirichlet(object):
    def __init__(self, alpha):
        '''Creates Dirichlet distribution with parameter `alpha`.'''
        from math import gamma
        from operator import mul
        self._alpha = np.array(alpha)
        self._coef = gamma(np.sum(self._alpha)) / \
                     np.multiply.reduce([gamma(a) for a in self._alpha])
    def pdf(self, x):
        '''Returns pdf value for `x`.'''
        from operator import mul
        return self._coef * np.multiply.reduce([xx ** (aa - 1)
                                                for (xx, aa)in zip(x, self._alpha)])
    def sample(self, N):
        '''Generates a random sample of size `N`.'''
        return np.random.dirichlet(self._alpha, N)

def draw_pdf_contours(dist, border=False, nlevels=200, subdiv=8, **kwargs):
    '''Draws pdf contours over an equilateral triangle (2-simplex).
    Arguments:
        `dist`: A distribution instance with a `pdf` method.
        `border` (bool): If True, the simplex border is drawn.
        `nlevels` (int): Number of contours to draw.
        `subdiv` (int): Number of recursive mesh subdivisions to create.
        kwargs: Keyword args passed on to `plt.triplot`.
    '''
    from matplotlib import ticker, cm
    import math

    refiner = tri.UniformTriRefiner(_triangle)
    trimesh = refiner.refine_triangulation(subdiv=subdiv)
    pvals = [dist.pdf(xy2bc(xy)) for xy in zip(trimesh.x, trimesh.y)]

    plt.figure(figsize=(25, 15))
    plt.tricontourf(trimesh, pvals, nlevels, cmap='jet', **kwargs)
    plt.axis('equal')
    plt.xlim(0, 1)
    plt.ylim(0, 0.75**0.5)
    plt.axis('off')
    if border is True:
        plt.triplot(_triangle, linewidth=1)

def plot_points(X, barycentric=True, border=True, **kwargs):
    '''Plots a set of points in the simplex.
    Arguments:
        `X` (ndarray): A 2xN array (if in Cartesian coords) or 3xN array
                       (if in barycentric coords) of points to plot.
        `barycentric` (bool): Indicates if `X` is in barycentric coords.
        `border` (bool): If True, the simplex border is drawn.
        kwargs: Keyword args passed on to `plt.plot`.
    '''
    if barycentric is True:
        X = X.dot(_corners)
    plt.plot(X[:, 0], X[:, 1], 'k.', ms=1, **kwargs)
    plt.axis('equal')
    plt.xlim(0, 1)
    plt.ylim(0, 0.75**0.5)
    plt.axis('off')
    if border is True:
        plt.triplot(_triangle, linewidth=1)

#if __name__ == '__main__':
#    f = plt.figure(figsize=(8, 6))
#    alphas = [[0.999] * 3,
#              [5] * 3,
#              [2, 5, 15]]
#    for (i, alpha) in enumerate(alphas):
#        plt.subplot(2, len(alphas), i + 1)
#        dist = Dirichlet(alpha)
#        draw_pdf_contours(dist)
#        title = r'$\alpha$ = (%.3f, %.3f, %.3f)' % tuple(alpha)
#        plt.title(title, fontdict={'fontsize': 8})
#        plt.subplot(2, len(alphas), i + 1 + len(alphas))
#        plot_points(dist.sample(5000))
#    plt.savefig('dirichlet_plots.png')
#    print('Wrote plots to "dirichlet_plots.png".')


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Apr  8 14:21:29 2021

@author: ariandovald
"""

import numpy as np
#from numba import jit

#MIDPOINT METHOD
def mid_one_d(steps, a, delta_x, f):
    '''uses midpoint method to calculate an integral given
    the starting point a, the number of steps, delta_x, and f'''
    i = 0
    for n in range(0, steps):
        x = a + (n * delta_x) #UPDATE X BY ADDING N * DELTA-X
        i += f(x + (delta_x * 0.5)) * delta_x #UPDATE INTEGRAL SUM USING MIDPOINT METHOD
    return i

#TRAPEZOIDAL ORDINAL SUM
#@jit(nopython = True)
def trapezoidal_ordsum(steps, a, b, a_new, b_new, delta_x, f) -> float:
    '''calculates a trapezoidal ordinate sum for a given function f and
    endpoints a and b'''
    ordsum = np.float64(0)
    x = a_new #SET STARTING POINT FOR X

    if a == a_new and b == b_new: #CHECK IF THIS IS THE FIRST ITERATION OR NOT
        ordsum += 0.5 * f(x) #ADD HALF OF THE STARTING ENDPOINT
        for n in range(1, steps-1):
            #REPEAT LOOP FROM AFTER STARTING ENDPOINT TO BEFORE ENDING ENDPOINT
            x = a + (n * delta_x) #UPDATE X BY ADDING N * DELTA_X
            ordsum += f(x) #ADD TO ORDINATE SUM
        ordsum += 0.5 * f(x + delta_x) #ADD HALF OF THE ENDING ENDPOINT
    else:
        for n in range(0, steps):
            #REPEAT LOOP FROM AFTER STARTING ENDPOINT TO BEFORE ENDING ENDPOINT
            if (x + (delta_x/2)) <= b:
                x = a_new + (n * delta_x) #UPDATE X BY ADDING N * DELTA_X
                ordsum += f(x) #ADD TO ORDINATE SUM

    return ordsum


import numpy as np
from Activation_function import sigmoid
from FeatureMapping import features_mapping

# TODO Cost function for linear regression for classification prob
def cost_function_linear(theta, X, y):
    # X: (m, n);  theta: (n, 1);  y: (m, 1)
    (m, n) = X.shape

    # Cost function
    J = 0
    h = np.matmul(X, theta)  # h: (m, 1)
    square = (h-y)**2
    J = 1/(2*m) * sum(square)

    return J

# TODO Cost function for logistic regression for classification prob
def cost_function_logistic(theta, X, y):
    # m: Number of training examples; n: Number of features
    # X: (m, n);  theta: (n, 1);  y: (m, 1)
    J = 0
    m, n = X.shape
    alpha = 0.0001
    grad = np.zeros(theta.shape)

    # (NOTE) Theta DIM must be (n, 1) (2D array))
    theta = np.reshape(theta, (n, 1))

    # Cost function
    h = sigmoid(np.dot(X, theta))  # h: (m, 1)
    J = (-1/m) * np.sum(np.dot(y.T, np.log(h)) + np.dot((1-y).T, np.log(1-h)))

    # Derivative of J (gradient descent)
    grad = (1/m) * (np.dot(X.T, (h-y)))

    return J, grad

# TODO Cost applied Regularization for classification logistic regression
# PURPOSE: Apply regularization for solving "Overfitting" problem
def cost_logistic_regularization(theta, X, y, lambdas):
    # X: (m, n+1);  theta: (n+1,)_1D array;  y: (m, 1), h: (m, 1)
    # Initial values
    theta = theta[:, np.newaxis]  # Increase 1D to 2D theta
    J = 0
    m = X.shape[0]
    grad = np.zeros((X.shape[1], 1))

    # Cost function apply regularization
    h = sigmoid(np.dot(X, theta))
    term = np.dot(y.T, np.log(h)) + np.dot((1-y).T, np.log(1-h))
    J = ((-1/m) * term) + (lambdas/(2*m)) * np.sum(theta[1:]**2)

    # Derivative of cost function (grad: (n+1, 1))
    grad = (1/m) * (np.dot(X.T, (h-y))) + (lambdas/m) * theta
    grad[0, 0] = (1/m) * np.dot(X.T[0, :], (h-y))

    return J, grad

# (NOTE) np.hstack(), np.vstack(): concatenate 2D array (horizontal & vertical)
# (NOTE) np.matmul() OR np.dot() return 2D array (If input 2D array)
# (NOTE) np.multiply() return 1D array (Even if input 2D array)


# ------------- Machine Learning - Topic 2: Logistic Regression

# You will need to complete the following functions
# in this exercise:
#     sigmoid
#     costFunction
#     predict
#     plotData
#     plotDecisionBoundary

## Initialization
import numpy as np
from scipy.optimize import fmin, fmin_bfgs
import os, sys
sys.path.append(os.getcwd() + os.path.dirname('/ml/ex2/'))
from helpers import sigmoid, costFunction, predict, plotData, plotDecisionBoundary

# Load Data
#  The first two columns contains the exam scores and the third column
#  contains the label.
data = np.loadtxt('ml/ex2/ex2data1.txt', delimiter=",")
X = data[:, :2]
y = data[:, 2]

## ==================== Part 1: Plotting ====================
#  We start the exercise by first plotting the data to understand the
#  the problem we are working with.
print('Plotting data with + indicating (y = 1) examples and o indicating (y = 0) examples.')

plt, p1, p2 = plotData(X, y)

# # Labels and Legend
plt.xlabel('Exam 1 score')
plt.ylabel('Exam 2 score')
plt.legend((p1, p2), ('Admitted', 'Not Admitted'), numpoints=1, handlelength=0)

plt.show(block=False) # prevents having to close the graph to move forward with ex2.py

input('Program paused. Press enter to continue.\n')
# plt.close()

## ============ Part 2: Compute Cost and Gradient ============
#  In this part of the exercise, you will implement the cost and gradient
#  for logistic regression. You neeed to complete the code in
#  costFunction.m

#  Setup the data matrix appropriately, and add ones for the intercept term
m, n = X.shape
X_padded = np.column_stack((np.ones((m,1)), X))

# Initialize fitting parameters
initial_theta = np.zeros((n + 1, 1))

# Compute and display initial cost and gradient
cost, grad = costFunction(initial_theta, X_padded, y, return_grad=True)

print('Cost at initial theta (zeros): {:f}'.format(cost))
print('Gradient at initial theta (zeros):')
print(grad)

input('Program paused. Press enter to continue.\n')

## ============= Part 3: Optimizing using fmin (and fmin_bfgs)  =============
#  In this exercise, you will use a built-in function (fmin) to find the
#  optimal parameters theta.

#  Run fmin and fmin_bfgs to obtain the optimal theta
#  This function will return theta and the cost
#  fmin followed by fmin_bfgs inspired by stackoverflow.com/a/23089696/583834
#  overkill... but wanted to use fmin_bfgs, and got error if used first
myargs=(X_padded, y)
theta = fmin(costFunction, x0=initial_theta, args=myargs)
theta, cost_at_theta, _, _, _, _, _ = fmin_bfgs(costFunction, x0=theta, args=myargs, full_output=True)

# Print theta to screen
print('Cost at theta found by fmin: {:f}'.format(cost_at_theta))
print('theta:'),
print(theta)

# Plot Boundary
plotDecisionBoundary(theta, X_padded, y)

plt.hold(False) # prevents further drawing on plot
plt.show(block=False)

input('Program paused. Press enter to continue.\n')

## ============== Part 4: Predict and Accuracies ==============
#  After learning the parameters, you'll like to use it to predict the outcomes
#  on unseen data. In this part, you will use the logistic regression model
#  to predict the probability that a student with score 45 on exam 1 and
#  score 85 on exam 2 will be admitted.
#
#  Furthermore, you will compute the training and test set accuracies of
#  our model.
#
#  Predict probability for a student with score 45 on exam 1
#  and score 85 on exam 2

prob = sigmoid(np.dot(np.array([1,45,85]),theta))
print('For a student with scores 45 and 85, we predict an admission probability of {:f}'.format(prob))

# Compute accuracy on our training set
p = predict(theta, X_padded)

print('Train Accuracy: {:f}'.format(np.mean(p == y) * 100))

input('Program paused. Press enter to continue.\n')


# write tests for bfs
import pytest
import numpy as np
from mst import Graph
from sklearn.metrics import pairwise_distances

def check_mst(adj_mat: np.ndarray,
              mst: np.ndarray,
              expected_weight: int,
              allowed_error: float = 0.0001):
    """ Helper function to check the correctness of the adjacency matrix encoding an MST.
        Note that because the MST of a graph is not guaranteed to be unique, we cannot
        simply check for equality against a known MST of a graph.

        Arguments:
            adj_mat: Adjacency matrix of full graph
            mst: Adjacency matrix of proposed minimum spanning tree
            expected_weight: weight of the minimum spanning tree of the full graph
            allowed_error: Allowed difference between proposed MST weight and `expected_weight`

        TODO:
            Add additional assertions to ensure the correctness of your MST implementation
        For example, how many edges should a minimum spanning tree have? Are minimum spanning trees
        always connected? What else can you think of?
    """
    def approx_equal(a, b):
        return abs(a - b) < allowed_error

    total = 0
    for i in range(mst.shape[0]):
        for j in range(i+1):
            total += mst[i, j]
    assert approx_equal(total, expected_weight), 'Proposed MST has incorrect expected weight'

    no_nodes = len(adj_mat)
    no_edges = np.count_nonzero(mst) / 2 #count the number of nonzero elements in mst. since this quantity represents twice the number of edges, dividing it by two will yield the number of edges in the mst
    assert no_edges == no_nodes - 1 #a mst will contain n-1 edges, where n is the number of nodes.

    rows,columns = np.nonzero(mst) #convert the adjacency matrix of the mst to that of an unweighted graph with the same connectivity
    unweighted = mst
    for ind in range(0,len(rows)):
        r = rows[ind]
        c = columns[ind]
        unweighted[r,c] = 1
    D = np.zeros((no_nodes,no_nodes)) #compute degree matrix corresponding to the MST
    for node in range(0,no_nodes):
        D[node,node] = np.count_nonzero(unweighted[:,node])

    laplacian = D - unweighted #compute the laplacian matrix corresponding to the unweighted MST
    eigs = np.sort(np.linalg.eig(laplacian)[0])[::-1]
    fiedler = eigs[-2] #if the MST is connected, the second smallest eigenvalue of its laplacian will be nonzero. https://en.wikipedia.org/wiki/Algebraic_connectivity
    assert fiedler > 0

def test_mst_small():
    """ Unit test for the construction of a minimum spanning tree on a small graph """
    file_path = './data/small.csv'
    g = Graph(file_path)
    g.construct_mst()
    check_mst(g.adj_mat, g.mst, 8)

def test_mst_single_cell_data():
    """ Unit test for the construction of a minimum spanning tree using
    single cell data, taken from the Slingshot R package
    (https://bioconductor.org/packages/release/bioc/html/slingshot.html)
    """
    file_path = './data/slingshot_example.txt'
    # load coordinates of single cells in low-dimensional subspace
    coords = np.loadtxt(file_path)
    # compute pairwise distances for all 140 cells to form an undirected weighted graph
    dist_mat = pairwise_distances(coords)
    g = Graph(dist_mat)
    g.construct_mst()
    check_mst(g.adj_mat, g.mst, 57.263561605571695)

def test_mst_student():
    """ TODO: Write at least one unit test for MST construction """

    #dummy network to test this
    file_path = './data/dummy.csv' #path to a connected graph of three nodes
    g = Graph(file_path)
    g.construct_mst()

    shape = np.shape(g.mst)

    assert shape == (3,3) #ensure that the mst adjacency matrix is square

    for row_ind in range(0,shape[0]):
        for column_ind in range(0,shape[1]):
            assert g.mst[row_ind,column_ind] == g.mst[column_ind,row_ind] #check that the mst adjacency matrix is symmetric


import sympy as sym
x, L, C, D, c_0, c_1, = sym.symbols('x L C D c_0 c_1')

class TwoPtBoundaryValueProblem(object):
    """
    Solve -(a*u')' = f(x) with boundary conditions
    specified in subclasses (method get_bc).
    a and f must be sympy expressions of x.
    """
    def __init__(self, f, a=1, L=L, C=C, D=D):
        """Default values for L, C, D are symbols."""
        self.f = f
        self.a = a
        self.L = L
        self.C = C
        self.D = D

        # Integrate twice
        u_x = - sym.integrate(f, (x, 0, x)) + c_0
        u = sym.integrate(u_x/a, (x, 0, x)) + c_1
        # Set up 2 equations from the 2 boundary conditions and solve
        # with respect to the integration constants c_0, c_1
        eq = self.get_bc(u)
        eq = [sym.simplify(eq_) for eq_ in eq]
        print(('BC eq:', eq))
        self.u = self.apply_bc(eq, u)

    def apply_bc(self, eq, u):
        # Solve BC eqs respect to the integration constants
        r = sym.solve(eq, [c_0, c_1])
        # Substitute the integration constants in the solution
        u = u.subs(c_0, r[c_0]).subs(c_1, r[c_1])
        u = sym.simplify(sym.expand(u))
        return u

    def get_solution(self, latex=False):
        return sym.latex(self.u, mode='plain') if latex else self.u

    def get_residuals(self):
        """Return the residuals in the equation and BCs."""
        R_eq = sym.diff(sym.diff(self.u, x)*self.a, x) + self.f
        R_0, R_L = self.get_bc(self.u)
        residuals = [sym.simplify(R) for R in (R_eq, R_0, R_L)]
        return residuals

    def get_bc(self, u):
        raise NotImplementedError(
            'class %s has not implemented get_bc' %
            self.__class__.__name__)

class Model1(TwoPtBoundaryValueProblem):
    """u(0)=0, u(L)=D."""
    def get_bc(self, u):
        return [u.subs(x, 0)-0,               # x=0 condition
                u.subs(x, self.L) - self.D]   # x=L condition

class Model2(TwoPtBoundaryValueProblem):
    """u'(0)=C, u(L)=D."""
    def get_bc(self, u):
        return [sym.diff(u,x).subs(x, 0) - self.C, # x=0 cond.
                u.subs(x, self.L) - self.D]        # x=L cond.

class Model3(TwoPtBoundaryValueProblem):
    """u(0)=C, u(L)=D."""
    def get_bc(self, u):
        return [u.subs(x, 0) - self.C,
                u.subs(x, self.L) - self.D]

class Model4(TwoPtBoundaryValueProblem):
    """u(0)=0, -u'(L)=C*(u-D)."""
    def get_bc(self, u):
        return [u.subs(x, 0) - 0,
                -sym.diff(u, x).subs(x, self.L) -
                self.C*(u.subs(x, self.L) - self.D)]

def test_TwoPtBoundaryValueProblem():
    f = 2
    model = Model1(f)
    print(('Model 1, u:', model.get_solution()))
    for R in model.get_residuals():
        assert R == 0

    f = x
    model = Model2(f)
    print(('Model 2, u:', model.get_solution()))
    for R in model.get_residuals():
        assert R == 0

    f = 0
    a = 1 + x**2
    model = Model3(f, a=a)
    print(('Model 3, u:', model.get_solution()))
    for R in model.get_residuals():
        assert R == 0

def demo_Model4():
    f = 0
    model = Model4(f, a=sym.sqrt(1+x))
    print(('Model 4, u:', model.get_solution()))

if __name__ == '__main__':
    test_TwoPtBoundaryValueProblem()
    demo_Model4()


import numpy as np
import matplotlib.pyplot as plt
import os
#figdir = os.path.join(os.environ["PYPROBML"], "figures")
figdir = "../figures";
def save_fig(fname): plt.savefig(os.path.join(figdir, fname))

from scipy.stats import nbinom

xs = np.arange(50);
fig, ax = plt.subplots(1,1)
p = 0.5; r = 1;
probabilities = nbinom.pmf(xs, r, p)
ax.bar(xs, probabilities)
ax.set_title('NB(r={:.2f}, p={:.2f})'.format(r, p))
save_fig('negbinomPlot_r{}_p{}.png'.format(int(r*10), int(p*10)))
plt.show()

fig, ax = plt.subplots(1,1)
p = 0.5; r = 10;
probabilities = nbinom.pmf(xs, r, p)
ax.bar(xs, probabilities)
ax.set_title('NB(r={:.2f}, p={:.2f})'.format(r, p))
save_fig('negbinomPlot_r{}_p{}.png'.format(int(r*10), int(p*10)))
plt.show()


"""
===================
Pyplot Two Subplots
===================

Create a figure with two subplots with `.pyplot.subplot`.
"""
import numpy as np
import matplotlib.pyplot as plt

def f(t):
    return np.exp(-t) * np.cos(2*np.pi*t)

t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)

plt.figure()
plt.subplot(211)
plt.plot(t1, f(t1), color='tab:blue', marker='o')
plt.plot(t2, f(t2), color='black')

plt.subplot(212)
plt.plot(t2, np.cos(2*np.pi*t2), color='tab:orange', linestyle='--')
plt.show()

#############################################################################
#
# .. admonition:: References
#
#    The use of the following functions, methods, classes and modules is shown
#    in this example:
#
#    - `matplotlib.pyplot.figure`
#    - `matplotlib.pyplot.subplot`


'''arange'''
# an arange returns evenly spaced values (where you give the space size) within a given interval and is part of the numpy package

#numpy.arange(start,stop,step)

#where start is a number that you want to start with, the interval includes this value
#stop is the end of the interval the interval does not include this number
#step is the spacing between values

#example
import numpy as numpy

a=numpy.arange(1,11,1)
print a

#this gives [1,2,3,4,5,6,7,8,9,10]

'''linspace'''

#linspace returns evenly spaced values (where you give the number of spaces) within a given interval and is part of the numpy package

#numpy.linspace(start,stop,num=x)

#where start is a number that you want to start with, the interval includes this value
#stop is the end of the interval the interval does include this number (unless you add endpoint=False,)
#num=x is the number of spaces you want and the interval will be split evenly amoung these spaces

b=numpy.linspace(0,10,num=5)
print b


import numpy as np
import time
import matplotlib.pyplot as plt
import sympy as sym

pts = [[0.1, 10.0], [0.2, 5.0], [0.30000000000000004, 3.333333333333333], [0.4, 2.5], [0.5, 2.0], [0.6, 1.6666666666666667], [0.7000000000000001, 1.4285714285714284], [0.8, 1.25], [0.9, 1.1111111111111112], [1.0, 1.0], [1.1, 0.9090909090909091], [1.2000000000000002, 0.8333333333333333], [1.3000000000000003, 0.769230769230769], [1.4000000000000001, 0.7142857142857142], [1.5000000000000002, 0.6666666666666665], [1.6, 0.625], [1.7000000000000002, 0.588235294117647], [1.8000000000000003, 0.5555555555555555], [1.9000000000000001, 0.5263157894736842], [2.0, 0.5], [2.1, 0.47619047619047616], [2.2, 0.45454545454545453], [2.3000000000000003, 0.4347826086956521], [2.4000000000000004, 0.41666666666666663], [2.5000000000000004, 0.3999999999999999], [2.6, 0.3846153846153846], [2.7, 0.37037037037037035], [2.8000000000000003, 0.3571428571428571], [2.9000000000000004, 0.3448275862068965], [3.0000000000000004, 0.33333333333333326], [3.1, 0.3225806451612903], [3.2, 0.3125], [3.3000000000000003, 0.303030303030303], [3.4000000000000004, 0.2941176470588235], [3.5000000000000004, 0.2857142857142857], [3.6, 0.2777777777777778], [3.7, 0.27027027027027023], [3.8000000000000003, 0.2631578947368421], [3.9000000000000004, 0.2564102564102564], [4.0, 0.25], [4.1, 0.24390243902439027], [4.2, 0.23809523809523808], [4.3, 0.23255813953488372], [4.3999999999999995, 0.2272727272727273], [4.5, 0.2222222222222222], [4.6, 0.2173913043478261], [4.7, 0.2127659574468085], [4.8, 0.20833333333333334], [4.9, 0.2040816326530612], [5.0, 0.2]]

def lagrange_eq(pts):
    n_equations = len(pts)
    crr_eq = list()

    for k in range(n_equations):
        crr_factors = list()
        for i in range(n_equations):
            if i != k:
                crr_factors.append(f'(x-{pts[i][0]})/({pts[k][0]}-{pts[i][0]})')
        crr_eq.append('*'.join(crr_factors) + '*' + str(pts[k][1]))

    eq_txt = '+'.join(crr_eq)
    eq = lambda x: eval(eq_txt)
    return eq

def lagrange_eq_lambdas(pts):
    n_equations = len(pts)
    crr_eq = list()
    x = sym.symbols('x')

    for k in range(n_equations):
        crr_factors = list()

        for i in range(n_equations):
            if pts[k][0] != pts[i][0]:
                kx1 = pts[k][0]
                ix1 = pts[i][0]
                crr_factors.append(((x-ix1) / (kx1-ix1)))

        yk = pts[k][1]
        eq = np.prod([subeq for subeq in crr_factors])
        eqq = eq * yk

        eqq = sym.lambdify(x, eqq)
        crr_eq.append(eqq)

    final_eq = lambda x: sum([e(x) for e in crr_eq])
    return final_eq

titxt = time.time()
P1 = lagrange_eq(pts)
print(P1(4))
print(P1(2.5))
print(f'Finished. Time necessary: {time.time()-titxt}')

tilambda = time.time()
P2 = lagrange_eq_lambdas(pts)
print(P2(4))
print(P2(2.5))
print(f'Finished. Time necessary: {time.time()-tilambda}')

# rango = np.linspace(1, 5, 1000)
# domain = [P2(x) for x in rango]
# print(len(rango))
# print(len(domain))
# correct = lambda x: 0.05*x**2 - 0.425*x + 1.15
#
#
# plt.plot(rango, domain)
# plt.plot(rango, correct(rango))
# plt.grid()
# plt.show()
#


import numpy as np
from scipy import linalg

def get_spectral_radius(W):
    """Given a squarred weight matrix (e.g. the recurrent reservoir matrix),
    returns the spectral radius"""
    return max(abs(linalg.eig(W)[0]))

def compute_error_NRMSE(teacher_signal, predicted_signal, verbose=False):
    """ Computes Normalized Root-Mean-Squarred Error between a teacher signal and a predicted signal
    Return the errors in this order: nmrse mean, nmrse max-min, rmse, mse.
    By default, only NMRSE mean should be considered as a general measure to be
    compared for different datasets.

    For more information, see:
    - Mean Squared Error https://en.wikipedia.org/wiki/Mean_squared_error
    - Root Mean Squared Error https://en.wikipedia.org/wiki/Root-mean-square_deviation for more info
    """
    mse = np.mean((teacher_signal - predicted_signal)**2)
    rmse = np.sqrt(mse)
    nmrse_mean = abs(rmse / np.mean(teacher_signal[:])) # Normalised RMSE (based on mean)
    nmrse_maxmin = rmse / abs(np.max(teacher_signal[:]) - np.min(teacher_signal[:])) # Normalised RMSE (based on max - min)
    return nmrse_mean, nmrse_maxmin, rmse, mse


import numpy as np
from numpy.linalg import eigh

# The probability of a Gaussian.
def px(x, mu, cov_inv, cov_det):
  N     = x.shape[0]
  d     = x.shape[1]
  coeff = 1.0/((2.0*np.pi)**(d/2.0) * cov_det**(0.5))

  result = np.zeros(N)
  for n in range(N):
    x_shifted = x[n,:] - mu
    result[n] = coeff * np.exp(-0.5 * x_shifted @ cov_inv @ x_shifted)

  return result

# Perform the Expectation-Maximization (EM) algorithm to
# find the maximum likelihood estimation of the parameters
# of a Gaussian-mixture model.
# Based on these lecture notes:
#  https://see.stanford.edu/materials/aimlcs229/cs229-notes8.pdf
#  https://www.ics.uci.edu/~smyth/courses/cs274/notes/EMnotes.pdf
# Also this video lecture is helpful:
#  https://www.youtube.com/watch?v=rVfZHWTwXSA
def em_gmm(data, num_gaussians, max_iter=100, rtol=1e-6, atol=1e-10, return_iterations=False):
  # The data matrix
  X = data

  # Number of samples
  N = data.shape[0]
  # Dimension of data
  d = data.shape[1]

  # The number of Gaussian in the Gaussian mixture model
  K    = num_gaussians
  # The probability of being in Gaussian l in {1,...,K} (initialized to uniform)
  phis = np.ones(K) / K
  # The mean vector of Gaussian l (initialized to first data points)
  mus = np.zeros((K, d))
  for k in range(K):
    mus[k,:] = X[k,:]
  # The covariance matrix of Gaussian l (initialized to data covariance)
  covs        = np.zeros((K, d, d))
  covs[0,:,:] = np.cov(X.T)
  for k in range(1,K):
    covs[k,:,:] = covs[0,:,:]

  if return_iterations:
    iteration_data = {
        "L"       : [],
        "mus"     : [],
        "covs"    : [],
        "phis"    : [],
        "weights" : []
      }

  log_likelihood_prev = np.inf
  weights             = np.zeros((N, K))
  for t in range(max_iter):
    for k in range(K):
      (evals, evecs) = eigh(covs[k,:,:])
      assert(np.all(evals > 1e-15))
      cov_det        = np.prod(evals)
      evals_inv      = np.array([(1.0/eval if np.abs(eval) > 1e-15 else 0.0) for eval in evals])
      cov_inv        = evecs @ np.diag(evals_inv) @ (evecs.conj().T)

      weights[:, k] = px(X, mus[k], cov_inv, cov_det) * phis[k]

    # Compute the log-likelihood.
    log_likelihood = np.sum(np.log(np.sum(weights, axis=1)))

    rel_change = np.abs(log_likelihood - log_likelihood_prev)/np.abs(log_likelihood)
    abs_change = np.abs(log_likelihood - log_likelihood_prev)

    print(f"=== Step {t+1} ===")
    print(f' L          = {log_likelihood}')
    print(f" rel_change = {rel_change}")
    print(f" abs_change = {abs_change}")

    if rel_change < rtol or abs_change < atol:
      break

    # Normalize the weights correctly.
    for n in range(N):
      weights[n, :] /= np.sum(weights[n, :])

    # Update the parameters.
    Nk   = np.sum(weights, axis=0)
    phis = Nk / N

    for i in range(d):
      mus[:, i] = (X[:,i] @ weights) / Nk

    for k in range(K):
      for i in range(d):
        x_shifted_i = X[:,i] - mus[k,i]
        for j in range(d):
          x_shifted_j   = X[:,j] - mus[k,j]
          covs[k, i, j] = ((x_shifted_i * x_shifted_j) @ weights[:,k]) / Nk[k]

    if return_iterations:
      iteration_data['L'].append(log_likelihood)
      iteration_data['mus'].append(np.copy(mus))
      iteration_data['covs'].append(np.copy(covs))
      iteration_data['phis'].append(np.copy(phis))
      iteration_data['weights'].append(np.copy(weights))

    log_likelihood_prev = log_likelihood

  if not return_iterations:
    return (log_likelihood, phis, mus, covs, weights)
  else:
    return (log_likelihood, phis, mus, covs, weights, iteration_data)


"""
Example of a gradient descent (ascent) implementation in tensorflow to find the discrete
probability distribution that solves the most simple one-step free energy optimization problem
of information-theoretic bounded rationality
Author: Sebastian Gottwald
Project: https://github.com/sgttwld/gradientdescent
"""

import numpy as np
import tensorflow as tf

# parameters
numEp = 50000
lr = .1
precision = 1e-8
N = 50
beta = 5.0

# constants
t = tf.constant(np.linspace(0,2,N))
U = -(t**2-2)**2/4 + 1
q = tf.constant(np.ones(N)/N)

# variables
theta = tf.Variable(np.ones(N),dtype=tf.float64)

# parametrization of p(t)
p = tf.exp(theta)/tf.reduce_sum(tf.exp(theta))

# objective
EU = tf.reduce_sum(p*U)    # expected utility E[U] = sum_x p(x) U(x)
DKL = tf.reduce_sum(p*tf.log(p/q))
obj = EU - DKL/beta

# optimizer and training operator
optimizer = tf.train.AdamOptimizer(learning_rate=lr,beta1=.9, beta2=.999,epsilon=1e-08,name='Adam')
#optimizer = tf.train.GradientDescentOptimizer(learning_rate=lr)
train_op = optimizer.minimize(-obj)

# initializer
init = tf.global_variables_initializer()

# running the TF session
with tf.Session() as sess:

    ## initializing
    sess.run(init)
    obj_curr = obj.eval(session=sess)
    obj0 = obj_curr

    print('f(t) = ', obj_curr)

    for n in range(0,numEp):

        ## run gradient descent
        sess.run(train_op)

        ## evaluation
        obj_curr = obj.eval(session=sess)
        p_curr = t.eval(session=sess)
        if n % 50 == 0:
            print('ep:',n,'F[p] = ', obj_curr)
        if abs(obj_curr-obj0) < precision:
            break
        else:
            obj0 = obj_curr

    prob = p.eval(session=sess)

# for command line visualization:
import hipsterplot as hplt

def show(v,stretch=2):
    hplt.plot(np.concatenate([[val]*stretch for val in v]), num_x_chars=stretch*len(v),num_y_chars=10)

print('utility')
show(-(np.linspace(0,2,N)**2-2)**2 / 4 + 1,stretch=1)

print('probability distribution')
show(prob,stretch=1)


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

# =====================generate Ellipse=====================
a = 6  # major axis
b = 2  # minor axis
x0 = 10  # center x0
y0 = 10  # center y0
N = 1000  # number of points

# angle for rotating ellipse data
theta = np.pi * 30 / 180

x, y = pytool.ellipse_surface(a, b, x0, y0, N, 'rand')
# x, y = pytool.ellipse_surface(a, b, x0, y0, N, 'order')

x = x - np.mean(x)
y = y - np.mean(y)

xse = [min(x), max(x)]
yse = [min(y), max(y)]

xy = np.array([x, y])
print(xy.shape)

A = xy

# ========================rotate============================
# ------------rotating matrix(Anti-clockwise)---------------
M = [[np.cos(theta), np.sin(theta)],
     [-np.sin(theta), np.cos(theta)]]
M = [[np.cos(theta), -np.sin(theta)],
     [np.sin(theta), np.cos(theta)]]
print("rotating matrix M: ", M)

# -----------------------rotating---------------------------
A = np.dot(M, A)

x = A[0]
y = A[1]

# ========================show data=========================
plt.figure()
plt.scatter(x, y, c='g', marker='o')
plt.grid()

# ==================Eigenvalue decomposition================

AAT = np.dot(A, A.transpose())
ATA = np.dot(A.transpose(), A)

evalues, evectors = np.linalg.eig(AAT)
# evalues, evectors = np.linalg.eig(ATA)
print(A.shape, AAT.shape, evalues.shape, evectors.shape)
print(AAT)
print("evalues: ", evalues, "evectors: ", evectors)
angle1 = np.arctan(evectors[1, 0] / evectors[0, 0]) * 180 / np.pi
angle2 = np.arctan(evectors[1, 1] / evectors[0, 1]) * 180 / np.pi
print(angle1, angle2)

# =====================plot eigen vector=====================
colorlines = ['-r', '-b']
pytool.plot_vectors2d(evectors.transpose(), xse=xse, yse=yse,
                      nPoints=N, title='eigen vectors', colorlines=colorlines)
plt.legend(['eigen vector1', 'eigen vector2'])
plt.show()

plt.figure()
plt.imshow(AAT)
plt.show()