import numpy as np
import pandas as pd

np.random.seed(0) # for reproduction

# CONVENIENT FUNCTIONS

def rand(a):
	"""
	Return uniform random number
	between -a and a, where a > 0
	"""
	return 2*a*np.random.rand() - a

def generate(type, **param):
	"""
	Return csv file at path with
	3 clusters following distribution type
	type = 'uniform':
	**param: C, R, N, path
	type = 'gaussian':
	**param: mean, cov, N, path
	"""
	if type == 'uniform':
		C = param.get('C')
		R = param.get('R')
		N = param.get('N')
		path = param.get('path')

		clusters = [[],[],[]]

		for i in range(3):
			for _ in range(N[i]):
				x = C[i][0] + rand(R[i])
				y = C[i][1] + rand(np.sqrt(R[i]**2 - (x - C[i][0])**2))
				clusters[i].append(np.array([x, y]))

		pd.DataFrame(clusters).to_csv(path,
			header = None, index = None)

	if type == 'gaussian':
		mean = param.get('mean')
		cov = param.get('cov')
		N = param.get('N')
		path = param.get('path')

		clusters = [[],[],[]]

		for i in range(3):
			for _ in range(N[i]):
				x, y = np.random.multivariate_normal(mean[i], cov[i])
				clusters[i].append(np.array([x, y]))

		pd.DataFrame(clusters).to_csv(path,
			header = None, index = None)

# DATA GENERATION

C = [[[0,0], [2,0], [0,2]],		# P1
	[[0,0], [3,0], [0, 1.5]],	# P2
	[[0,0], [2,0], [0,2]],		# P3
	[[0,0], [3,0], [0, 1.5]]]	# P4

R = [[1, 1, 1],		# P1
	[1, 2, .5],		# P2
	[1, 1, 1],		# P3
	[1, 2, .5]]		# P4

N = [[100, 100, 100],	# P1
	[100, 100, 100],	# P2
	[100, 200, 100],	# P3
	[100, 200, 100]]	# P4

for problem in range(4):

	p = str(problem + 1)

	generate(type = 'uniform', C = C[problem],
		R = R[problem], N = N[problem],
		path = 'P' + p + '/' + 'P' + p + '.csv')


import numpy as np

def dist_pll_lines_2d(m, b1, b2):
    """Finding the distance between two parallel lines.
    The distance between two parallel lines is the distance between the points where a perpendicular line intersects.
    This function will find that distance (d). The parameters of the function can be obtained from the equation of a parallel
    line y = mx + b.
    Parameters
    ----------
    m  : float, int
        slope of two parallel lines which are the same, i.e., slope of
        line 1, m1 = -(1/m2)
    b1 : float, int
        intercept of line 1 where y = mx + b1
    b2 : float
        intercept of line 2 where y = mx + b2
    Returns
    -------
    float
        The distance d between two parallel lines.
    Examples
    --------
    >>> dist_pll_lines_2d(3.0, 4.5, 2.5)
    0.6324
    >>> dist_pll_lines_2d(-4, 11, 23)
    2.9104
    """
    # apply corresponding values from the two parallel line equations to
    # the equation below
    if not isinstance(m, (int, float)):
        raise TypeError("m must be an integer or float")

    if not isinstance(b1, (int, float)):
        raise TypeError("b1 must be an integer or float")

    if not isinstance(b2, (int, float)):
        raise TypeError("b2 must be an integer or float")

    d = abs(b2 - b1) / np.sqrt(m ** 2 + 1)
    return d

def get_distance(x1, x2, metric="Euclidean", p=None):
    """Calculates the distance between two n dimensional vectors.
    Possible metrics include: Euclidean, Manhattan, Chebyshev, or Minkowski.
    Parameters
    ----------
    x1 : list of int or float
        The first n dimensional vector.
    x2 : list of int or float
        The second n dimensional vector.
    metric : str, default="Euclidean"
        The distance metric, must be one of "Euclidean", "Manhattan", Chebyshev,
        or "Minkowski"
    p : int, default=None
        The order of Minkowski distance to calculate.  Only required if `metric` is
        set to "Minkowski".
    Returns
    -------
    float
        The relevant distance between the two vectors.
    Examples
    --------
    >>> x1 = [1, 2, 3, 4]
    >>> x2 = [5, 6, 7, 8]
    >>> get_distance(x1, x2, metric="Euclidean")
    8
    >>> get_distance(x1, x2, metric="Manhattan")
    16
    >>> get_distance(x1, x2, metric="Chebyshev")
    4
    >>> get_distance(x1, x2, metric="Minkowski", 3)
    6.3496
    """

    # cast to lower for flexibility
    metric = metric.lower()

    # verify distance metric
    if metric not in ["euclidean", "manhattan", "chebyshev", "minkowski"]:
        raise ValueError(f"Invalid distance metric: {metric}")

    # verify that vectors are passed as python lists
    if not isinstance(x1, list) or not isinstance(x2, list):
        raise TypeError("x1 and x2 must be Python lists")

    # verify dimensions of vectors match
    if len(x1) != len(x2):
        raise TypeError("x1 and x2 must be the same length")

    # verify p if minkowski distance is metric
    if metric.lower() == "minkowski":
        if not isinstance(p, (int, float)):
            raise TypeError("p must be int or float")

    # convert to numpy arrays
    x1 = np.array(x1)
    x2 = np.array(x2)

    # check if arrays are equal if so distance is 0
    if np.array_equal(x1, x2):
        return 0
    elif metric == "euclidean":
        return np.sqrt(np.sum((np.square(x1 - x2))))
    elif metric == "manhattan":
        return np.sum(np.abs(x1 - x2))
    elif metric == "chebyshev":
        return np.max(np.abs(x1 - x2))
    elif metric == "minkowski":
        return np.power(np.sum(np.power(np.abs(x1 - x2), p)), 1 / p)

def is_intersection_3d(m1, b1, m2, b2):
    """Determines whether two infinite lines intersect in 3-dimensional space.
    Note that if two parallel lines are provided, they will be considered as NOT intersecting.
    Also note that this function expects integer values for x, y, z coordinates. Values will be rounded
    if integer values are not provided.
    This algorithm uses the following idea to test for intersection: Two (non parallel) lines intersect
    in 3d space if and only if they are coplanar.
    Parameters
    ----------
    m1 : list or tuple of floats
        This list corresponds to a 3-dimensional vector ⟨𝑚𝑥1,𝑚𝑦1,𝑚𝑧1⟩ describing the
        direction vector (or slope) of line 2. List or tuple must be of length 3.
    b1 : list or tuple of floats
        Any point on line 1. This list corresponds to a point (x1, y1, z1) that lies on line 1.
        This point must lie on line 2.
    m2 : list or tuple of floats
        This list corresponds to a 3-dimensional vector ⟨𝑚𝑥2,𝑚𝑦2,𝑚𝑧2⟩ describing the
        direction vector (or slope) of line 2.
    b2 : list or tuple of floats
        Any point on line 2. This list corresponds to a point (x2, y2, z2) that lies on line 2.
        This point must lie on line 2.
    Returns
    -------
    bool
        True if there is intersection, False if not.
    Examples
    --------
    >>> m1 = (1, 0, 0)
    >>> m2 = (0, 1, 0)
    >>> b1 = (0, 0, 0)
    >>> b2 = (0, 0, 0)
    >>> is_intersection_3d(m1, b1, m2, b2)
    True
    >>> m3 = (1, 3, -1)
    >>> m4 = (2, 1, 4)
    >>> b3 = (0, -2, 4)
    >>> b4 = (0, 3, -3)
    >>> is_intersection_3d(m3, m4, b3, b4)
    False
    """

    # Validate input
    if not isinstance(m1, (list, tuple) or not isinstance(m2, (list, tuple))):
        raise TypeError(
            "Only lists or tuples are supported. Please provide m1 & m2 as lists or tuples"
        )

    if not isinstance(b1, (list, tuple)) or not isinstance(b1, (list, tuple)):
        raise TypeError(
            "Only lists or tuples are supported. Please provide b1 & b2 as lists or tuples."
        )

    if len(m1) != 3 or len(m2) != 3 or len(b1) != 3 or len(b2) != 3:
        raise ValueError("All tuples should of length 3. Please check your inputs")

    # Ensure all values are integers
    m1 = (round(m1[0]), round(m1[1]), round(m1[2]))
    m2 = (round(m2[0]), round(m2[1]), round(m2[2]))
    b1 = (round(b1[0]), round(b1[1]), round(b1[2]))
    b2 = (round(b2[0]), round(b2[1]), round(b2[2]))

    m1 = np.array(m1)
    m2 = np.array(m2)
    b1 = np.array(b1)
    b2 = np.array(b2)

    # Check if lines are parallel
    if (m1 == m2).all():
        return False

    # Lines intersect if and only if they lie on the same plane (and are not parallel).
    x = np.cross(m1, m2)
    disp = np.array(b2) - np.array(b1)
    if np.dot(x, disp) == 0:
        return True
    else:
        return False

def is_orthogonal(m1, m2):
    """Determines whether two infinite lines are perpendicular in n-dimensional space.
    Parameters
    ----------
    m1 : list or tuple of floats
        This list corresponds to a n-dimensional vector ⟨mx1, my1, mz1, ...⟩ describing the
        direction vector of line 1.
    m2 : list or tuple of floats
        This list corresponds to a n-dimensional vector ⟨mx2, my2, mz2, ...⟩ describing the
        direction vector of line 2. Demensions of line 1 and line 2 must be equal.
    Returns
    -------
    bool
        True if there is orthogonal, False if not.
    Examples
    --------
    >>> m1 = (1, 0)
    >>> m2 = (0, 1)
    >>> is_orthogonal(m1, m2)
    True
    >>> m3 = (0, 0, 1)
    >>> m4 = (1, 1, 1)
    >>> is_orthogonal(m3, m4)
    False
    """
    # Validate input
    if not isinstance(m1, (list, tuple) or not isinstance(m2, (list, tuple))):
        raise TypeError("m1 and m2 must be lists or tuples")

    if len(m1) < 1 or len(m2) < 1:
        raise ValueError("The dimension of inputs must be at least 2")

    # Checking if m1 and m2 have the same dimension
    if len(m1) != len(m2):
        raise ValueError("m1 and m2 must be the same length")

    line1 = np.array(m1)
    line2 = np.array(m2)
    if round(np.dot(line1, line2), 10) == 0:
        return True
    else:
        return False


'''
Functions for getting input and performing
main operations.
'''
from sys import exit as leave
from math import cos, radians
import numpy as np

def read_input():
    '''
    Returns user's input.
    '''
    #Getting user's year.
    year = int(input('Enter a year you are interested in (must be integer number): '))

    try:
        year = int(year)

    #Excepting an event when users gives wrong year number.
    except ValueError:
        print('     THE YEAR VALUE MUST BE INTEGER NUMBER    ')
        leave()

    try:
        #Getting coordinates of the place.
        lattitude, longtitude = input('Enter lattitude and longtitude separated with a coma \
(example: 50.456322,98.23423423): ').split(',')

    #Excepting an event when users gives only one number.
    except ValueError:
        print('         THERE MUST BE TWO VALUES IN INPUT FIELD!!!        ')
        leave()
    try:
        lattitude, longtitude = float(lattitude), float(longtitude)

    #Excepting an event when users gives wrong numbers.
    except ValueError:
        print(' THE COORDINATES VALUES MUST BE FLOAT NUMBERS ')
        leave()

    return (year, lattitude, longtitude)

def find_distances(locations_dataframe, point: tuple):
    '''
    Returns a dataframe of distances between the given point
and locations from dataframe.
    '''

    lattitude, longtitude = point
    lattitude, longtitude = radians(lattitude), radians(longtitude)
    locations_dataframe['lattitude_radians'] = np.radians(locations_dataframe['lattitude'])
    locations_dataframe['longtitude_radians'] = np.radians(locations_dataframe['longtitude'])
    locations_dataframe['delta_longtitude'] = locations_dataframe['longtitude_radians'] - longtitude
    locations_dataframe['delta_lattitude'] = locations_dataframe['lattitude_radians'] - lattitude
    locations_dataframe['half_chord'] = np.sin(locations_dataframe['delta_lattitude']/2)**2 + \
                                        cos(lattitude) * \
                                        np.cos(locations_dataframe['lattitude_radians']) * \
                                        np.sin(locations_dataframe['delta_longtitude']/2)**2
    locations_dataframe['distances'] = 6367 * 2 * np.arctan2(
                                                  np.sqrt(locations_dataframe['half_chord']),
                                                  np.sqrt(1 - locations_dataframe['half_chord']))

    return locations_dataframe

def find_closes_locations(locations_dataframe):
    '''
    Returns 10 closest locations.

    >>> import pandas as pd
    >>> closest_locations_dataframe = pd.DataFrame(columns = { \
                                                  'distances' \
                                                  })
    >>> closest_locations_dataframe['distances'] = [100, 90, 80, 30, \
                                                    20, 10, 5, 5, 4, \
                                                    3, 2, 4, 54, 33]
    >>> find_closes_locations(closest_locations_dataframe)
        distances
    10          2
    9           3
    8           4
    11          4
    6           5
    7           5
    5          10
    4          20
    3          30
    13         33
    '''
    top_closest_points = locations_dataframe.nsmallest(10, 'distances',  keep = 'all')
    return top_closest_points[:10]

if __name__ == '__main__':
    import doctest
    doctest.testmod()


import numpy as np
import pandas as pd

class ROUGH_SETS:

  def __init__(self, filename, A):
    self.filename = filename
    self.A = A

  def get_table(self):
    df = pd.read_csv(self.filename, delimiter = " ")
    return df

  #1. To provide partition of the tuples of the table for a given attribute
  def get_partition(self, attribute):
    df = self.get_table()
    attribute_values = df[['ID', attribute]].values.tolist()
    #print(attribute_values)

    attribute_types = np.unique(df[attribute].values.tolist())
    #print(attribute_types)

    partition = []
    temp = []

    for t in attribute_types:
      for i in attribute_values:
        if ( t == i[1] ):
          temp.append(i[0])
      partition.append(temp)
      temp = []

    del attribute_values
    del attribute_types
    del temp
    del t
    del i

    return partition

  #2. Using the function of task 1, compute partitions for all the attributes of the table
  def all_partitions(self):
    df = self.get_table()
    for attribute in df.columns:
      print("partition w.r.t " + attribute, get_partition(df, attribute))

  #3. To produce a simple set of given set A
  def simple_set(self, setA):
    return set(setA)

  #4. To produce frequency table of elements of given set A
  def freq_table(self, setA):
    f_table = []
    for s in setA:
      f_table.append( [ s, list(setA).count(s) ] )

    f_df = pd.DataFrame(f_table)

    del f_table
    del s

    return f_df

  #5. To get the cardinality of a given set A
  def get_cardinality(self, setA):
    return len(setA)

  #6. To get the cardinality of the corresponding simple set of a given set A.
  def cardinality_simple_set(self, setA):
    return len(set(setA))

  #7. To get the complement of a given set A.
  def complement(self, setA, U):
    return U - setA

  #8. To produce (a) intersection, (b) Union, (c) set difference of given two sets A and B
  def intersection(self, setA, setB):
    return setA & setB

  def union(self, setA, setB):
    return setA | setB

  def set_difference(self, setA, setB):
    return setA - setB

  #9. To develop boolean functions for testing whether (a) is A a subset of B  (b) is A equals to B
  def is_subset(self, setA, setB):
    return setA.issubset(setB)

  def is_equal(self, setA, setB):
    return setA == setB

  '''
  10. To develop a boolean flag vector corresponding to elements of a given partition P,
  (a) is A  a super set of element of P
  (b) is A has a non-trivial intersection with the element of P.
  '''
  def is_superset(self, partitionP):
    vector = []
    for element in partitionP:
      if ((self.A.issuperset(element)) == True):
        vector.append(1)
      else:
        vector.append(0)
    del element

    return vector

  def nt_intersection(self, partitionP):
    vector = []
    for element in partitionP:
      if ( len(self.A & set(element)) == 0 ):
        vector.append(0)
      else:
        vector.append(1)
    del element

    return vector

  '''
  11. To obtain the union of elements of the given partition P for which the flags are one (true) based on
  (a) super set flags
  (b) non-trivial intersection flags for a given set A
  The resultant sets will be referred as Lower Approximation of A and Upper Approximation of A respectively with respective to the partition P.
  '''
  def get_LSA(self, partitionP):
    vector = self.is_superset(partitionP)

    LSA = set()
    for index, v in enumerate(vector):
      if (v == 1):
        LSA = self.union(LSA, set(partitionP[index]))

    return LSA

  def get_USA(self, partitionP):
    vector = self.nt_intersection(partitionP)

    USA = set()
    for index, v in enumerate(vector):
      if (v == 1):
        USA = self.union(USA, set(partitionP[index]))

    return USA

  '''
  12. Develop an algorithm which returns Lower and Upper Approximations of given set A
  with respective to given Partition P.  Name this function as f_LUSA(A,P)
  let us denote the return values of the algorithm as LSA and USA
  '''
  def f_LUSA(self, A, P):
    self.A = A
    LSA = self.get_LSA(P)
    USA = self.get_USA(P)

    return LSA, USA

  #13. To obtain the set difference of USA and LSA. Say this resultant set as the Boundary set of A denoted as BNA.
  def get_BNA(self, P):
    LSA, USA = self.f_LUSA(self.A, P)
    BNA = self.set_difference(USA, LSA)

    return BNA

  #14.  To obtain the complement set of USA. Say this resultant set as a Negative set of A denoted as NGA.
  def get_NGA(self, P, U):
    USA = self.get_USA(P)
    return self.complement(USA, U)

  '''
  15. Develop an algorithm to get the Cardinality of sets A, U, LSA, USA,  BNA and NGA
  with respective to a Partition P obtained from  an attribute
  of a given Information System or Decision System. Say the output of this algorithm
  as n_A, N_U, n_LSA, n_USA, n_BNA and n_NGA  respectively.
  '''
  def cardinality_sets(self, attribute, U):

    P = self.get_partition(attribute)

    LSA, USA = self.f_LUSA(self.A, P)

    n_A = self.get_cardinality(A)
    n_U = self.get_cardinality(U)
    n_LSA = self.get_cardinality(LSA)
    n_USA = self.get_cardinality(USA)
    n_BNA = self.get_cardinality(self.get_BNA(P))
    n_NGA = self.get_cardinality(self.get_NGA(P, U))

    return n_A, n_U, n_LSA, n_USA, n_BNA, n_NGA

  #16. To verify that LSA, BNA and NGA of given set A with respect to a given Partition P is a partition of U, the Universal set.
  def verify_partition(self, A, P, U):
    self.A = A

    LSA = self.get_LSA(P)

    BNA = self.get_BNA(P)

    NGA = self.get_NGA(P, U)

    if ( ( len( LSA & BNA ) == 0 ) and ( len( BNA & NGA ) == 0 ) and ( len( NGA & LSA ) == 0 ) and ( LSA | BNA | NGA == U )):
      return True
    else:
      return False

###

filename = 'info_table.txt'
A = {1, 2, 3}
U = {1, 2, 3, 4, 5, 6}

rough_set = ROUGH_SETS(filename, A)

P = rough_set.get_partition("AOI")
rough_set.cardinality_sets("AOI", U)
print(rough_set.verify_partition(A, P, U))


# implementation of numerical gradient optimization, trying to choose the best
# inputs (x0, x1) that will minimize the function f. This is done by starting
# with two random inputs, and then probing the function to find the slope around
# these random inputs. In other words, we compute the derivatives (dx0, dx1)
# which are numbers that indicates how the function f changes in response to a
# tiny change in x0, x1. Then, we walk against that slope in the direction of
# steepest descent - we add some amount to our initial inputs such that the f
# function is expected to be lower. Repeat for a 1000 iterations and stop. The
# final inputs will be the local minima of the function, if one exists.
import numpy as np
np.random.seed(1) # constant seed for reproducible results

# f(x0,x1) - the function we want to minimize. It can be anything you want! Note
# however, that if this function doesn't have a minima, it can get inifinitely
# negative, and thus no minima will be found even after infinite iterations. So,
# for this demonstration, we'll remove negatives by squaring it. It can be
# assumed that this function is unknown, and resides as a compiled black-box and
# may contain arbitrary and complicated logic
def f(x0, x1):
    return (x0 - x1) ** 2

# constants - these can be learned as well! Maybe we'll cover it later.
E = 0.0001 # epsilon; infinitisimal size of probes to find derivatives
ALPHA = 0.01 # size of the steps to take in the gradient direction
ITERATIONS = 150 # number of probes/step to take

# initial values in [-10 .. +10]
x0 = np.random.rand()
x1 = np.random.rand()

for i in xrange(ITERATIONS):
    y = f(x0, x1)
    print "%s: f(%f, %f) = %f" % (i, x0, x1, y)

    # two samples, inifinitsimal points around x0 and x1
    yx0 = f(x0 + E, x1)
    yx1 = f(x0, x1 + E)

    # derivatives of x0 and x1 - or how the output of f changes w.r.t x0, x1
    dx0 = (yx0 - y) / E
    dx1 = (yx1 - y) / E

    # update to the new x, y in the gradient direction
    x0 += ALPHA * dx0 * -1 # -1 because we're after the minima, we want to walk
    x1 += ALPHA * dx1 * -1 # downwards against the slope


import numpy as np

def threshold_array(X, threshold):
    """Soft thresholding or shrinkage operator"""
    X_thresh = np.sign(X) * np.maximum(np.abs(X) - threshold, 0)
    return X_thresh

def l1_norm(X):
    return np.abs(X).sum()

def soft_threshold(X, gamma=None, eps=1e-11):
    if not gamma:
        gamma = np.sqrt(X.shape[0] * X.shape[1])

    if l1_norm(X) < gamma:
        return X

    lower = 0
    upper = np.max(np.abs(X))
    while (upper - lower) > eps:
        mid = (lower + upper) / 2
        mid_norm = l1_norm(threshold_array(X, mid))
        if mid_norm > gamma:
            lower = mid
        else:
            upper = mid
    X_thresh = threshold_array(X, mid)
    return X_thresh

def proportion_variance_explained(X, Y, center=False):
    if center:
        X = X.copy()
        X -= X.mean(axis=0)[None, :]
    return (np.linalg.norm(X @ Y @ np.linalg.inv(Y.T @ Y) @ Y.T, ord="fro") ** 2) / (
        np.linalg.norm(X, ord="fro") ** 2
    )

def calculate_explained_variance_ratio(X, Y, center=False):
    n_components = Y.shape[1]
    explained_variance_ratio = []
    for i in range(1, n_components + 1):
        pve = proportion_variance_explained(X, Y[:, :i])
        explained_variance_ratio.append(pve)
    return np.array(explained_variance_ratio)


import numpy as np
import time as tm
import sys

def PrettyPrintComplexMatrix(matrix,prec=3,linewidth1=150,suppressOn=True):
  np.set_printoptions(precision=prec,linewidth=linewidth1,suppress=suppressOn)
  print('RE=\n', np.real(matrix))
  print('Im=\n', np.imag(matrix))

def matrixMatrixMultiplication_good(A,B):
  return np.dot(A,B)

def matrixMatrixMultiplication_dummy(A,B):
  N = len(A)
  C = np.zeros((N,N),dtype=complex)
  for ii in range(0,N):
    for jj in range(0,N):
      for kk in range(0,N):
        C[ii,jj] += A[ii,kk]*B[kk,jj]
  return C

N = int(sys.argv[1])
print(N)

A = np.zeros((N,N),dtype=complex)
B = np.zeros((N,N),dtype=complex)

omega = np.exp(2.j*np.pi/N)
omega_i  = 1.
omega_ij = 1.

# A is the Discrete Fourier Transform (DFT) matrix.
for ii in range(0,N):
  for jj in range(0,N):
    A[ii,jj] = omega_ij
    B[jj,ii] = np.conj(A[ii,jj])/N
    omega_ij *= omega_i
  omega_i *= omega

print('\nA=\n')
PrettyPrintComplexMatrix(A)
print('\nB=\n')
PrettyPrintComplexMatrix(B)

t1 = tm.time()
C = matrixMatrixMultiplication_good(A,B)

print('\nC=\n')
PrettyPrintComplexMatrix(C)

print('\n\ntime for matrix multiplication: ', tm.time() - t1)


import scipy as sp
import numpy as np

from rosetta.modeling import eda

# Define the logit function that acts on series to get the logit of the series
def logit(series):
    """
    Logit for Pandas.Series
    """
    logseries = np.log(series)
    return logseries / (1 - logseries)

def logit_of_mean(series):
    """
    Logit of the mean of a pandas series
    """
    mean = series.mean()
    return logit(mean)

def sigmoidize(x, scale=5, mid=None):
    """
    Returns y, a sigmoidal version of the variable x.
    y = sig((x - mid) / scale)
    sig(z) = exp(z) / (1 + exp(z))

    Parameters
    ----------
    x : np.ndarray, ndim=1
    scale : positive real number
    mid : real number
        If None, use the mean
    """
    y = x.copy()
    if not mid:
        mid = y.mean()

    z = (x - mid) / float(scale)
    exp_z = np.exp(z)

    return exp_z / (1 + exp_z)

def standardize(x):
    """
    Standardizes a Series or DataFrame.
    """
    return (x - x.mean()) / x.std()

def build_xy_for_linearize(
    x, y, bins=10, Y_reducer=np.mean, x_lims=None, endpoints=None):
    """
    Return x and y for use in linearization.  Use with var_create.interp.

    Parameters
    ----------
    x : Pandas.Series
    y : Pandas.Series
    bins : positive integer
        Number of bins for x
    Y_reducer : Function
        Used to reduce Y in each of the bins.  E.g np.mean, logit_of_mean.
    x_lims : 2-tuple, (xmin, xmax)
        Rescaled x will be constant outside of this range.
        Choose xmin, xmax such that you have enough data in the interval
        (xmin, xmax)
    endpoints : Array-like
        [xmin, xmax, ymin, ymax].  Makes sure F(xmin) = ymin, etc...

    Returns
    -------
    x : Array
        The bin midpoints (with adjunstments at the ends)
    y : Array
        y reduced in the bins
    """
    # Trim the range of x used
    x_actualmin = x.min()
    x_actualmax = x.max()
    if x_lims:
        # We need to keep the actual max/min
        mask = (x > x_lims[0]) & (x < x_lims[1])
    else:
        mask = np.ones(len(x), dtype=bool)

    # reduced_Y is the reduced y values with an index equal to the x midpoints
    reduced_Y, _ = eda.reducedY_vs_binnedX(
        x[mask], y[mask], Y_reducer=Y_reducer, bins=bins)

    # If we don't convert to float, we get an object series...
    x_midpts = reduced_Y.index.values.astype('float')

    # Stick on the endpoints
    if endpoints is None:
        endpoints = x_actualmin, x_actualmax, reduced_Y[0], reduced_Y[-1]
    x_extended = np.r_[endpoints[0], x_midpts, endpoints[1]]
    y_extended = np.r_[endpoints[2], reduced_Y, endpoints[3]]

    return x_extended, y_extended

def interp(x, y, t=1, scaling=None):
    """
    Return interpolation helpers for x and y.
    See build_xy_for_linearize for use in linearization.

    Parameters
    ----------
    x : Array-like
    y : Array-like
    t : Real number in [0, 1]
        With F(x) the linearization function, re-set F(x) = t*F(x) + (1-t)*x
    scaling : String
        If None, the output is not rescaled and Y_reducer(bin_j) = x_j where
            x_j is the midpoint of bin_j.
        If 'standardize', then output will have zero mean and unit variance
        If 'unit', then output will be on the interval [0, 1]

    Returns
    -------
    F_x : Pandas.Series
        A rescaled version of x.
    F : Function that will rescale x
        Cannot be pickled... :(

    Examples
    --------
    x4linear, y4linear = vc.build_xy_for_linearize(y_score, y)
    F_x, F = interp(x4linear, y4linear)
    """
    # Interpolate to get our first try at F
    F_1 = sp.interpolate.interp1d(x, y, kind='linear')

    # Reshape
    F_2 = lambda x: t * F_1(x) + (1 - t) * x

    # Scaling
    F_2_x = F_2(x)
    if scaling is not None:
        if scaling == 'standardize':
            F_3 = lambda x: (F_2(x) - F_2_x.mean()) / F_2_x.std()
        elif scaling == 'unit':
            F_3 = lambda x: (
                F_2(x) - F_2_x.min()) / (F_2_x.max() - F_2_x.min())
        else:
            raise ValueError("Unknown scaling passed: %s" % scaling)
    else:
        F_3 = F_2

    return F_3(x), F_3


"""
Utils Functions for Pose Transformation
Author: Chongzhao Mao, Lingxiao Song
"""

import numpy as np
from transforms3d.euler import quat2euler
from pyquaternion import Quaternion
import cv2

def compute_norm(v):
    """
    Compute norm of a vector.

    Args:
        v: numpy array of arbitrary length.
    Returns:
        norm: float, norm of vector.
    """
    norm = np.linalg.norm(v)
    return norm

def quat_to_euler(quat):
    """
    Convert quaternion to euler representation.

    Args:
        quat: 4D numpy array.
    Returns:
        euler: 3D numpy array.
    """
    euler = np.array(quat2euler(quat))
    return euler

def quat_to_degree(quat):
    """
    Convert quaternion to degree representation.

    Args:
        quat: 4D numpy array.
    Returns:
        degree: 3D numpy array.
    """
    return quat_to_euler(quat) / np.pi * 180

def compute_angle_two_quat(quat_0, quat_1, out_dim=1):
    """
    Compute angle in degree of two quaternions.

    Args:
        quat_0: 4D numpy array.
        quat_1: 4D numpy array.
        out_dim: (optional) 1 or 3. 1 for norm of 3 dimension, 3 for each dimension.
    Returns:
        degree_diff: 1D or 3D numpy array.
    """
    degree_0 = quat_to_degree(quat_0)
    degree_1 = quat_to_degree(quat_1)
    degree_diff = np.abs(degree_0 - degree_1)
    degree_diff = np.minimum(degree_diff, 360 - degree_diff)
    if out_dim == 1:
        degree_diff = compute_norm(degree_diff)
    elif out_dim == 3:
        pass
    return degree_diff


import numpy as np

def norm(x, axis=None):
    return np.linalg.norm(x, axis=axis)

#-------------------------
#----- Poincaré Disk -----
#-------------------------

# NOTE: POSSIBLE ISSUE WITH DIFFERENT WAYS TO SPECIFY MINKOWSKI DOT PRODUCT
# arbritray sign gives different signatures (+, +, +, -), (+, -, -, -)

# distance in poincare disk
def poincare_dist(u, v, eps=1e-5):
    d = 1 + 2 * norm(u-v)**2 / ((1 - norm(u)**2) * (1 - norm(v)**2) + eps)
    return np.arccosh(d)

# compute symmetric poincare distance matrix
def poincare_distances(embedding):
    n = embedding.shape[0]
    dist_matrix = np.zeros((n, n))
    for i in range(n):
        for j in range(i+1, n):
            dist_matrix[i][j] = poincare_dist(embedding[i], embedding[j])
    return dist_matrix

# convert array from poincare disk to hyperboloid
def poincare_pts_to_hyperboloid(Y, eps=1e-6, metric='lorentz'):
    mink_pts = np.zeros((Y.shape[0], Y.shape[1]+1))
    # print('Minkowski pts shape: {}'.format(mink_pts.shape))
    r = norm(Y, axis=1)
    if metric == 'minkowski':
        mink_pts[:, 0] = 2/(1 - r**2 + eps) * (1 + r**2)/2
        for i in range(1, mink_pts.shape[1]):
            mink_pts[:, i] = 2/(1 - r**2 + eps) * Y[:, i - 1]
    else:
        mink_pts[:, Y.shape[1]] = 2/(1 - r**2 + eps) * (1 + r**2)/2
        for i in range(0, Y.shape[1]):
            mink_pts[:, i] = 2/(1 - r**2 + eps) * Y[:, i]
    """
    if metric == 'minkowski':
        mink_pts[:, 0] = 2/(1 - r**2 + eps) * (1 + r**2)/2
        mink_pts[:, 1] = 2/(1 - r**2 + eps) * Y[:, 0]
        mink_pts[:, 2] = 2/(1 - r**2 + eps) * Y[:, 1]
    else:
        mink_pts[:, 0] = 2/(1 - r**2 + eps) * Y[:, 0]
        mink_pts[:, 1] = 2/(1 - r**2 + eps) * Y[:, 1]
        mink_pts[:, 2] = 2/(1 - r**2 + eps) * (1 + r**2)/2
    """
    return mink_pts

# convert single point to hyperboloid
def poincare_pt_to_hyperboloid(y, eps=1e-6, metric='lorentz'):
    mink_pt = np.zeros((y.shape[0] + 1, ))
    # print('mink_pt.shape: {}'.format(mink_pt.shape))
    r = norm(y)

    if metric == 'minkowski':
        mink_pt[0] = 2/(1 - r**2 + eps) * (1 + r**2)/2
        for i in range(1, mink_pt.shape[0]):
            mink_pt[i] = 2/(1 - r**2 + eps) * y[i - 1]
    else:
        mink_pt[y.shape[0]] = 2/(1 - r**2 + eps) * (1 + r**2)/2
        for i in range(0, y.shape[0]):
            mink_pt[i] = 2/(1 - r**2 + eps) * y[i]
    """
    mink_pt = np.zeros((3, ))
    r = norm(y)
    if metric == 'minkowski':
        mink_pt[0] = 2/(1 - r**2 + eps) * (1 + r**2)/2
        mink_pt[1] = 2/(1 - r**2 + eps) * y[0]
        mink_pt[2] = 2/(1 - r**2 + eps) * y[1]
    else:
        mink_pt[0] = 2/(1 - r**2 + eps) * y[0]
        mink_pt[1] = 2/(1 - r**2 + eps) * y[1]
        mink_pt[2] = 2/(1 - r**2 + eps) * (1 + r**2)/2
    """
    return mink_pt

#------------------------------
#----- Hyperboloid Model ------
#------------------------------

# NOTE: POSSIBLE ISSUE WITH DIFFERENT WAYS TO SPECIFY MINKOWSKI DOT PRODUCT
# arbritray sign gives different signatures (+, +, +, -), (+, -, -, -)

# define hyperboloid bilinear form
def hyperboloid_dot(u, v):
    # print('U dim: {}'.format(u.shape))
    # print('V dim: {}'.format(v.shape))
    return np.dot(u[:-1], v[:-1]) - u[-1]*v[-1]

# define alternate minkowski/hyperboloid bilinear form
def minkowski_dot(u, v):
    return u[0]*v[0] - np.dot(u[1:], v[1:])

# hyperboloid distance function
def hyperboloid_dist(u, v, eps=1e-6, metric='lorentz'):
    if metric == 'minkowski':
        dist = np.arccosh(-1*minkowski_dot(u, v))
    else:
        dist = np.arccosh(-1*hyperboloid_dot(u, v))
    if np.isnan(dist):
        #print('Hyperboloid dist returned nan value')
        return eps
    else:
        return dist

# compute symmetric hyperboloid distance matrix
def hyperboloid_distances(embedding):
    n = embedding.shape[0]
    dist_matrix = np.zeros((n, n))
    for i in range(n):
        for j in range(i+1, n):
            dist_matrix[i][j] = hyperboloid_dist(embedding[i], embedding[j])
    return dist_matrix

# convert array to poincare disk
def hyperboloid_pts_to_poincare(X, eps=1e-6, metric='lorentz'):
    poincare_pts = np.zeros((X.shape[0], X.shape[1]-1))
    if metric == 'minkowski':
        for i in range(0, X.shape[1]):
            poincare_pts[:, i] = X[:, i + 1] / ((X[:, 0] + 1) + eps)
    else:
        for i in range(0, X.shape[1]):
            poincare_pts[:, i] = X[:, i] / ((X[:, -1] + 1) + eps)
    """
    if metric == 'minkowski':
        poincare_pts[:, 0] = X[:, 1] / ((X[:, 0]+1) + eps)
        poincare_pts[:, 1] = X[:, 2] / ((X[:, 0]+1) + eps)
    else:
        poincare_pts[:, 0] = X[:, 0] / ((X[:, 2]+1) + eps)
        poincare_pts[:, 1] = X[:, 1] / ((X[:, 2]+1) + eps)
    """
    return poincare_pts

# project within disk
def proj(theta,eps=1e-3):
    if norm(theta) >= 1:
        theta = theta/norm(theta) - eps
    return theta

# convert single point to poincare
def hyperboloid_pt_to_poincare(x, eps=1e-6, metric='lorentz'):
    poincare_pt = np.zeros((x.shape[0] - 1, ))
    if metric == 'minkowski':
        for i in range(0, poincare_pt.shape[0]):
            poincare_pt[i] = x[i + 1] / ((x[0] + 1) + eps)
    else:
        for i in range(0, poincare_pt.shape[0]):
            poincare_pt[i] = x[i] / ((x[-1] + 1) + eps)
    """
    poincare_pt = np.zeros((2, ))
    if metric == 'minkowski':
        poincare_pt[0] = x[1] / ((x[0]+1) + eps)
        poincare_pt[1] = x[2] / ((x[0]+1) + eps)
    else:
        poincare_pt[0] = x[0] / ((x[2]+1) + eps)
        poincare_pt[1] = x[1] / ((x[2]+1) + eps)
    """
    return proj(poincare_pt)

# helper function to generate samples
def generate_data(n, radius=0.7, hyperboloid=False):
    theta = np.random.uniform(0, 2*np.pi, n)
    u = np.random.uniform(0, radius, n)
    r = np.sqrt(u)
    x = r * np.cos(theta)
    y = r * np.sin(theta)
    init_data = np.hstack((x.reshape(-1,1), y.reshape(-1,1)))
    if hyperboloid:
        return poincare_pts_to_hyperboloid(init_data)
    else:
        return init_data


from typing import Union, cast

import numpy as np

def angle_between_vectors(v1: np.ndarray, v2: np.ndarray) -> float:
    """angle_between_vectors returns the angle in radians between two vectors.

    Args:
        v1 (np.ndarray): Vector 1 of shape (N)
        v2 (np.ndarray): Vector 2 of same shape as ``v1``

    Returns:
        float: angle in radians
    """
    cos_ang = np.dot(v1, v2)
    sin_ang = np.linalg.norm(np.cross(v1, v2))
    return cast(float, np.arctan2(sin_ang, cos_ang))

def compute_yaw_around_north_from_direction(direction_vector: np.ndarray) -> float:
    """compute_yaw_from_direction computes the yaw as angle between a 2D input direction vector and
the y-axis direction vector (0, 1).

    Args:
        direction_vector (np.ndarray): Vector of shape (2,)

    Returns:
        float: angle to (0,1) vector in radians
    """
    return angle_between_vectors(direction_vector, np.array([0.0, 1.0]))

def angular_distance(angle_a: Union[float, np.ndarray], angle_b: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
    """ A function that takes two arrays of angles in radian and compute the angular distance, wrap the angular
    distance such that they are always in the [-pi, pi) range.

    Args:
        angle_a (np.ndarray, float): first array of angles in radians
        angle_b (np.ndarray, float): second array of angles in radians

    Returns:
        angular distance in radians between two arrays of angles
    """

    return (angle_a - angle_b + np.pi) % (2 * np.pi) - np.pi


# -*- coding: utf-8 -*-
"""
Created on Tue Jul 10 16:35:22 2018

@author: SilverDoe
"""

'''

The scipy.spatial package can compute Triangulations, Voronoi Diagrams and Convex Hulls of
a set of points, by leveraging the Qhull library. Moreover, it contains KDTree implementations
for nearest-neighbor point queries and utilities for distance computations in various metrics.

'''

'''======================= Delaunay Triangulations ============================

In mathematics and computational geometry, a Delaunay triangulation for a given set P of
discrete points in a plane is a triangulation DT(P) such that no point in P is inside the
circumcircle of any triangle in DT(P).

'''
import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial import Delaunay

points = np.array([[0, 4], [2, 1.1], [1, 3], [1, 2]])
tri = Delaunay(points)

tri.points
tri.convex_hull

plt.triplot(points[:,0], points[:,1], tri.simplices.copy())
plt.plot(points[:,0], points[:,1], 'o')
plt.show()

#=================== finding coplanar points =================================
from scipy.spatial import Delaunay
points = np.array([[0, 0], [0, 1], [1, 0], [1, 1], [1, 1]])
tri = Delaunay(points)
print(tri.coplanar)

'''==================== Convex Hulls ==========================================

In mathematics, the convex hull or convex envelope of a set of points X in the Euclidean plane
or in a Euclidean space (or, more generally, in an affine space over the reals) is the smallest
convex set that contains X.

'''
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
points = np.random.rand(10, 2) # 30 random points in 2-D
hull = ConvexHull(points)

plt.plot(points[:,0], points[:,1], 'o')
for simplex in hull.simplices:
    plt.plot(points[simplex,0], points[simplex,1], 'k-')
plt.show()

'''

>> The conventional 2D or 3D (or for that matter nD) space that we regularly use is called the Euclidean space,
   also called the flat space.

>> Why is it called so? Because all the classical laws of geometry that we are aware of (Pythagoras theorem,
   properties of polygons, etc.) are only valid (in the way we know them) in flat spaces. What it means is
   that that the line joining any two points in this space is straight.

>> Imagine your computer screen. Draw two points anywhere on it (key : ‘Imagine’). Now the shortest way of
   reaching one point from the other is a straight line, because this space is flat.

>> Now imagine those two points on the surface of a basketball. For simplicity, on diametrically opposite
   ends (on poles, if you prefer it). There are infinite ways to reach one point from the other, but the
   shortest is the semicircle that connects the points; since this ‘space’ is curved. Obviously, the
   Pythagorean theorem wouldn’t work here (since no straight lines connecting three non-linear points).

>> Therefore this concept of flat space as opposed to curved space exists. And the flat space is conventionally
   called the Euclidean space because all of Euclidean geometry was done in flat space.

'''


import time
import numpy as np
from scipy import special
import matplotlib.pyplot as plt
pi = np.pi

def ExactValueOfSphereVolume(Dimension = 3, Radius = 1.0):
    Dim = Dimension
    ExactValue = ( pi**(Dim/2.0) * Radius**Dim ) / special.gamma( Dim/2.0+1.0 )
    return ExactValue
#-----------------------

def getEstimateOfSphereVolume(NumSamples, Dimension = 3, Radius = 1.0, InitialSeed = -1):
    Dim = Dimension
    if InitialSeed >= 0:
        np.random.seed(InitialSeed)
    else:
        np.random.seed()
    BoxVolume = (2.0 * Radius)**Dim
    estimate = []
    inside = 0
    total = 0
    for i in range(0,NumSamples):
        sum = 0.0
        for d in range(Dim):
            x = np.random.uniform(-Radius,Radius)
            sum += x*x
        sum = np.sqrt(sum)
        if(sum <= Radius): inside += 1
        total += 1
        ratio = np.float(inside)/np.float(total)
        value = ratio * BoxVolume
        estimate.append(value)
    estimate = np.array(estimate)
    return np.copy(estimate)
#----------------------------

NumRuns = 20
NumSamples = 1000000
Dimension = 5
ExactValue = ExactValueOfSphereVolume(Dimension = Dimension)

FigOffset = 0.5

y_min = ExactValue - FigOffset
y_max = ExactValue + FigOffset
plt.axis([0, NumSamples, y_min, y_max])

for i in range(NumRuns):
    estimate = getEstimateOfSphereVolume(NumSamples = NumSamples, Dimension = Dimension)
    plt.plot(estimate)
    if (i+1) % 10 == 0: print 'Run {:4d} of {:4d}'.format(i+1,NumRuns)
plt.plot([0, NumSamples], [ExactValue, ExactValue], 'r--')
plt.show()


# -----------------------------------------------------------------------------
# From https://en.wikipedia.org/wiki/Minkowski–Bouligand_dimension:
#
# In fractal geometry, the Minkowski–Bouligand dimension, also known as
# Minkowski dimension or box-counting dimension, is a way of determining the
# fractal dimension of a set S in a Euclidean space Rn, or more generally in a
# metric space (X, d).
# -----------------------------------------------------------------------------
# code taken from https://gist.github.com/rougier/e5eafc276a4e54f516ed5559df4242c0
import numpy as np

def fractal_dimension(b_image):
    """
    Calculates the fractal dimension of the given binary image Z
    :param b_image: a binary 2d image
    :return: the Minkowski–Bouligand dimension of the image
    """

    # Only for 2d image
    assert(len(b_image.shape) == 2)

    # From https://github.com/rougier/numpy-100 (#87)
    def boxcount(Z, k):
        S = np.add.reduceat(
            np.add.reduceat(Z, np.arange(0, Z.shape[0], k), axis=0),
                               np.arange(0, Z.shape[1], k), axis=1)

        # We count non-empty (0) and non-full boxes (k*k)
        return len(np.where((S > 0) & (S < k*k))[0])

    # Minimal dimension of image
    p = min(b_image.shape)

    # Greatest power of 2 less than or equal to p
    n = 2**np.floor(np.log(p)/np.log(2))

    # Extract the exponent
    n = int(np.log(n)/np.log(2))

    # Build successive box sizes (from 2**n down to 2**1)
    sizes = 2**np.arange(n, 1, -1)

    # Actual box counting with decreasing size
    counts = []
    for size in sizes:
        counts.append(boxcount(b_image, size))

    # Fit the successive log(sizes) with log (counts)
    coeffs = np.polyfit(np.log(sizes), np.log(counts), 1)
    return -coeffs[0]

# I = scipy.misc.imread("sierpinski.png")/256.0
# print("Minkowski–Bouligand dimension (computed): ", fractal_dimension(I))
# print("Haussdorf dimension (theoretical):        ", (np.log(3)/np.log(2)))


#!/usr/bin/python

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

matplotlib.rcParams.update({'font.size': 12})

circle1 = plt.Circle((0,0),1, color='magenta', alpha=0.4)

fig,ax= plt.subplots()

ax.add_artist(circle1)

ax.set_xlim(-5.2,5.2)
ax.set_ylim(-5.2,5.2)
ax.set_xlabel('X', fontsize=12)
ax.set_ylabel('Y', fontsize=12)

xp = np.linspace(-10,10,num=1000)

print xp

kval = [-3,-2,-1,0,1,2,4,8.125,11]

yp0 = [(8*(x**2) - kval[0])/2.0 for x in xp]
yp1 = [(8*(x**2) - kval[1])/2.0 for x in xp]
yp2 = [(8*(x**2) - kval[2])/2.0 for x in xp]
yp3 = [(8*(x**2) - kval[3])/2.0 for x in xp]
yp4 = [(8*(x**2) - kval[4])/2.0 for x in xp]
yp5 = [(8*(x**2) - kval[5])/2.0 for x in xp]
yp6 = [(8*(x**2) - kval[6])/2.0 for x in xp]
yp7 = [(8*(x**2) - kval[7])/2.0 for x in xp]
yp8 = [(8*(x**2) - kval[8])/2.0 for x in xp]

#print yp

plt.plot(xp, yp0,color='hotpink',label=r'$f(x,y)=-3.0$')
plt.plot(xp, yp1,color='red',linestyle='-.',label=r'$f(x,y)=-2.0$')
plt.plot(xp, yp2,color='olive',label=r'$f(x,y)=-1.0$')
plt.plot(xp, yp3,color='lime',label=r'$f(x,y)=0.0$')
#plt.plot(xp, yp4,color='aqua',label=r'$f(x,y)=1.0$')
plt.plot(xp, yp5,color='aqua',label=r'$f(x,y)=2.0$')
#plt.plot(xp, yp5,color='deepskyblue',label=r'$f(x,y)=4.0$')
plt.plot(xp, yp7,color='navy',linestyle='--',label=r'$f(x,y)=8.125$')
plt.plot(xp, yp8,color='gray',label=r'$f(x,y)=11.0$')

plt.axhline(y=0, linestyle=':', linewidth=1, color='orange')
plt.axhline(y=1, linestyle=':', linewidth=1, color='orange')
plt.axhline(y=-1, linestyle=':', linewidth=1, color='orange')

plt.axvline(x=0, linestyle=':', linewidth=1, color='orange')
plt.axvline(x=1, linestyle=':', linewidth=1, color='orange')
plt.axvline(x=-1, linestyle=':', linewidth=1, color='orange')

plt.annotate('Minima',color='red',xy=(-0.49, 2.34), xytext=(-3.0,2.24), ha='center', arrowprops=dict(facecolor='red', edgecolor='red',shrink=0.05,  width=1))
plt.annotate('Maxima',color='navy',xy=(0.67, -2.), xytext=(3.0,-2.05), ha='center', arrowprops=dict(facecolor='navy', edgecolor='navy',shrink=0.05,  width=1))

#print  Ypara1

plt.title ('Example of Solving Equation with Constraint; Lagrange Multiplier')
plt.legend(fontsize=12)
plt.show()


from typing import Tuple

import numpy as np

def gamma_logpdf(x: float, k: float, theta: float) -> float:
    """Log-density of the Gamma distribution up to a constant factor.

    Args:
        x: Positive number at which to evaluate the Gamma distribution.
        k: Shape parameter of the Gamma distribution.
        theta: Scale parameter of the Gamma distribution.

    Returns:
        out: The log-density of the Gamma distribution.

    """
    return (k - 1.0)*np.log(x) - x / theta

def grad_gamma_logpdf(x: float, k: float, theta: float) -> float:
    """Gradient of the log-density of the Gamma distribution.

    Args:
        x: Positive number at which to evaluate the Gamma distribution.
        k: Shape parameter of the Gamma distribution.
        theta: Scale parameter of the Gamma distribution.

    Returns:
        out: The gradient of the log-density of the Gamma distribution.

    """
    return (k - 1.0) / x - np.reciprocal(theta)

def hess_gamma_logpdf(x: float, k: float, theta: float) -> float:
    """Hessian of the log-density of the Gamma distribution.

    Args:
        x: Positive number at which to evaluate the Gamma distribution.
        k: Shape parameter of the Gamma distribution.
        theta: Scale parameter of the Gamma distribution.

    Returns:
        out: The Hessian of the log-density of the Gamma distribution.

    """
    return -(k - 1.0) / np.square(x)

def grad_hess_gamma_logpdf(x: float, k: float, theta: float) -> float:
    """Third-order derivatives of the log-density of the Gamma distribution.

    Args:
        x: Positive number at which to evaluate the Gamma distribution.
        k: Shape parameter of the Gamma distribution.
        theta: Scale parameter of the Gamma distribution.

    Returns:
        out: The third-order derivative of the log-density of the Gamma
            distribution.

    """
    return 2.0*(k - 1.0) / np.power(x, 3.0)

def log_prior(sigmasq: float, beta: float) -> float:
    """The log-prior of the log-Gaussian Cox-Poisson model.

    Args:
        sigmasq: Amplitude of the Gaussian process kernel.
        beta: Length scale of the Gaussian process kernel.

    Returns:
        lp: The log-density of the prior distribution.

    """
    lp = gamma_logpdf(beta, 2.0, 0.5)
    lp += gamma_logpdf(sigmasq, 2.0, 0.5)
    return lp

def grad_log_prior(phis: float, phib: float) -> Tuple[float]:
    """Gradient of the log-prior with respect to the reparameterized model
    parameters that are unconstrained.

    Args:
        phis: Reparameterized aplitude of the Gaussian process kernel.
        phib: Reparameterized length scale of the Gaussian process kernel.

    Returns:
        out: The gradient of the log-prior with respect to the reparameterized
            model parameters.

    """
    sigmasq = np.exp(phis)
    beta = np.exp(phib)
    dphis = grad_gamma_logpdf(sigmasq, 2.0, 0.5) * sigmasq
    dphib = grad_gamma_logpdf(beta, 2.0, 0.5) * beta
    return np.array((dphis, dphib))

def hess_log_prior(phis: float, phib: float) -> np.ndarray:
    """Compute the hessian of the log-prior with respect to the reparameterized
    model parameters.

    Args:
        phis: Reparameterized aplitude of the Gaussian process kernel.
        phib: Reparameterized length scale of the Gaussian process kernel.

    Returns:
        H: The Hessian of the log-prior with respect to the reparameterized model
            parameters.

    """
    sigmasq = np.exp(phis)
    beta = np.exp(phib)
    H = np.array([
        [grad_gamma_logpdf(sigmasq, 2.0, 0.5)*sigmasq + np.square(sigmasq)*hess_gamma_logpdf(sigmasq, 2.0, 0.5), 0.0],
        [0.0, grad_gamma_logpdf(beta, 2.0, 0.5)*beta + np.square(beta)*hess_gamma_logpdf(beta, 2.0, 0.5)]
    ])
    return H

def grad_hess_log_prior(phis: float, phib: float) -> np.ndarray:
    """Compute the third-order derivatives of the log-prior with respect to the
    reparameterized model parameters.

    Args:
        phis: Reparameterized aplitude of the Gaussian process kernel.
        phib: Reparameterized length scale of the Gaussian process kernel.

    Returns:
        dH: The third-order derivatives of the log-prior.

    """
    sigmasq = np.exp(phis)
    beta = np.exp(phib)
    dH = np.zeros((2, 2, 2))
    a = sigmasq*grad_gamma_logpdf(sigmasq, 2.0, 0.5)
    a += np.square(sigmasq)*hess_gamma_logpdf(sigmasq, 2.0, 0.5)
    a += 2.0*sigmasq*hess_gamma_logpdf(sigmasq, 2.0, 0.5)
    a += np.square(sigmasq)*grad_hess_gamma_logpdf(sigmasq, 2.0, 0.5)
    b = beta*grad_gamma_logpdf(beta, 2.0, 0.5)
    b += np.square(beta)*hess_gamma_logpdf(beta, 2.0, 0.5)
    b += 2.0*beta*hess_gamma_logpdf(beta, 2.0, 0.5)
    b += np.square(beta)*grad_hess_gamma_logpdf(beta, 2.0, 0.5)
    dH = np.array([
        [[a, 0.0], [0.0, 0.0]],
        [[0.0, 0.0], [0.0, b]]
    ])
    return dH


import matplotlib.pyplot as plt
from matplotlib import patches
import numpy as np

########################################################################
# 3.1

def p1():

    x1 = [0 for i in range(n)]
    y1 = [0 for i in range(n)]

    radius1 = np.random.uniform(0, radius_limit, n)
    theta1 = np.random.uniform(0, 2*np.pi, n)

    for i in range(n):

        x1[i] = radius1[i] * np.cos(theta1[i])
        y1[i] = radius1[i] * np.sin(theta1[i])

    print("Variation in X coordinates 3.1 -> ", round(np.var(x1), 2))

    plt.scatter(x1, y1, s=1, alpha=1, c=colors, cmap='twilight_shifted')

    circ = plt.Circle((0, 0), radius_limit, color='black', fill=False)
    fig = plt.gcf()
    axes = fig.gca()
    axes.add_patch(circ)

    plt.xlim(-radius_limit*1.2, radius_limit*1.2)
    plt.ylim(-radius_limit*1.2, radius_limit*1.2)

    axes = plt.gca()
    axes.set_aspect('equal')

    plt.show()

########################################################################
# 3.2

def p2():

    x2 = [0 for i in range(n)]
    y2 = [0 for i in range(n)]

    for i in range(n):

        tempx = np.random.uniform(-radius_limit, radius_limit, 1)
        tempy = np.random.uniform(-radius_limit, radius_limit, 1)

        while (np.sqrt(tempx**2 + tempy**2) > radius_limit):

            tempx = np.random.uniform(-radius_limit, radius_limit, 1)
            tempy = np.random.uniform(-radius_limit, radius_limit, 1)

        x2[i] = tempx
        y2[i] = tempy

    print("Variation in X coordinates 3.2-> ", round(np.var(x2), 2))

    plt.scatter(x2, y2, s=1, alpha=1, c=colors, cmap='twilight_shifted')

    circ = plt.Circle((0, 0), radius_limit, color='black', fill=False)
    fig = plt.gcf()
    axes = fig.gca()
    axes.add_patch(circ)

    plt.xlim(-radius_limit*1.2, radius_limit*1.2)
    plt.ylim(-radius_limit*1.2, radius_limit*1.2)

    axes = plt.gca()
    axes.set_aspect('equal')

    plt.show()

########################################################################
# 3.3

def p3():

    x3 = [0 for i in range(n)]
    y3 = [0 for i in range(n)]

    theta3 = np.random.uniform(0, 2*np.pi, n)

    for i in range(n):

        radius3 = np.sqrt(np.random.random()) * radius_limit

        x3[i] = radius3 * np.cos(theta3[i])
        y3[i] = radius3 * np.sin(theta3[i])

    print("Variation in X coordinates 3.3 -> ", round(np.var(x3), 2))

    plt.scatter(x3, y3, s=1, alpha=1, c=colors, cmap='twilight_shifted')

    circ = plt.Circle((0, 0), radius_limit, color='black', fill=False)
    fig = plt.gcf()
    axes = fig.gca()
    axes.add_patch(circ)

    plt.xlim(-radius_limit*1.2, radius_limit*1.2)
    plt.ylim(-radius_limit*1.2, radius_limit*1.2)

    axes = plt.gca()
    axes.set_aspect('equal')

    plt.show()

########################################################################
# Question 3

n = 50000

colors = np.array([i for i in range(n)])

radius_limit = int(input("Input radius limit for all questions: "))

p1()
p2()
p3()


import numpy as np
import matplotlib.pyplot as plt

def model_A(x, params):
    y = params[0] + x*params[1] + params[2]*x**2
    return y

def model_B(x, params):
    y = params[0]*(np.exp(-0.5*(x-params[1])**2/params[2]**2))
    return y

def model_C(x, params):
    y = params[0]*(np.exp(-0.5*(x-params[1])**2/params[2]**2))
    y += params[0]*(np.exp(-0.5*(x-params[3])**2/params[4]**2))
    return y

def logprior_model_A(params):
    return 0

def logprior_model_B(params):
    if params[2]<0:
        return -np.inf
    else:
        return 0

def logprior_model_C(params):
    if params[2]<0 or params[4]<0 or params[1]<0 or params[3]<0:
        return -np.inf
    else:
        return 0

def loglike(x_obs, y_obs, sigma_y_obs, model, params):
    l = np.sum(-0.5*(y_obs -model(x_obs, params))**2/sigma_y_obs**2)
    return l
def run_mcmc(x_obs, y_obs, sigma_y_obs,
             model, logprior, n_params=3, n_iterations=20000, scale=0.1):

    params = np.ones([n_iterations, n_params])
    loglike_values = np.ones(n_iterations)*5.0
    loglike_values[0] = loglike(x_obs, y_obs, sigma_y_obs, model, params[0,:])
    for i in range(1, n_iterations):
        current_params = params[i-1,:]
        next_params = current_params + np.random.normal(scale=scale, size=n_params)

        loglike_current = loglike(x_obs, y_obs, sigma_y_obs, model, current_params) + logprior(current_params)
        loglike_next = loglike(x_obs, y_obs, sigma_y_obs, model, next_params) + logprior(next_params)

        r = np.min([np.exp(loglike_next - loglike_current), 1.0])
        alpha = np.random.random()

        if alpha < r:
            params[i,:] = next_params
            loglike_values[i] = loglike_next
        else:
            params[i,:] = current_params
            loglike_values[i] = loglike_current

    params = params[n_iterations//2:,:]
    loglike_values = loglike_values[n_iterations//2:]
    return {'params':params, 'x_obs':x_obs, 'y_obs':y_obs, 'loglike_values':loglike_values}

def BIC(params_model):
    max_loglike = np.max(params_model['loglike_values'])
    n_dim = np.shape(params_model['params'])[1]
    n_points = len(params_model['y_obs'])
    return 2.0*(-max_loglike + 0.5*n_dim*np.log(n_points))

def plot_model(params_model, model, model_name):
    n_dim = np.shape(params_model['params'])[1]
    n_points = len(params_model['y_obs'])

    plt.figure(figsize = (4*(n_dim//2+1),6))
    for i in range(n_dim):
        plt.subplot(2, n_dim//2+1, i+1)
        plt.hist(params_model['params'][:,i], density=True, bins=30)
        plt.title(r"$\beta_{}= {:.2f}\pm {:.2f}$".format(i,np.mean(params_model['params'][:,i]), np.std(params_model['params'][:,i])))
        plt.xlabel(r"$\beta_{}$".format(i))

    plt.subplot(2,n_dim//2+1, i+2)
    best = np.mean(params_model['params'], axis=0)
    x = params_model['x_obs']
    x_model = np.linspace(x.min(), x.max(), 100)
    y_model = model(x_model, best)
    plt.plot(x_model, y_model)
    plt.errorbar(x, y, yerr=sigma_y, fmt='o')
    plt.title("BIC={:.2f}".format(BIC(params_model)))

    plt.subplots_adjust(hspace=0.5)
    plt.savefig(model_name+".png")

# Lee los datos
data = np.loadtxt('data_to_fit.txt')
x = data[:,0]
y = data[:,1]
sigma_y = data[:,2]

# ajusta los modelos
params_model_A = run_mcmc(x, y, sigma_y, model_A, logprior_model_A, scale=0.05, n_iterations=1000000)
params_model_B = run_mcmc(x, y, sigma_y, model_B, logprior_model_B, scale=0.05, n_iterations=1000000)
params_model_C = run_mcmc(x, y, sigma_y, model_C, logprior_model_C, n_params=5, scale=0.05, n_iterations=1000000)

# hace las graficas
plot_model(params_model_A, model_A, 'model_A')
plot_model(params_model_B, model_B, 'model_B')
plot_model(params_model_C, model_C, 'model_C')


import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg

mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 200)
z = np.linspace(-2, 2, 200)
r = z**2 + 1
x = r * np.sin(2*theta)
y = r * np.cos(2*theta)
ax.plot(x, y, z, label='parametric curve')
ax.legend()

plt.savefig('3d_parametric_curve.png')
plt.show()


import matplotlib.pyplot as plt
import scipy
import numpy as np
import networkx as nx
import random
import scipy.io
import scipy.spatial.distance as sd

def is_connected(adj,n):
# Uses the fact that multiplying the adj matrix to itself k times give the
# number of ways to get from i to j in k steps. If the end of the
# multiplication in the sum of all matrices there are 0 entries then the
# graph is disconnected. Computationally intensive, but can be sped up by
# the fact that in practice the diameter is very short compared to n, so it
# will terminate in order of log(n)? steps.
    adjn=np.zeros((n,n))
    adji=adj.copy()
    for i in range(n):
        adjn+=adji
        adji=adji.dot(adj)
    return len(np.where(adjn == 0)[0])==0

def max_span_tree(adj):
    n=adj.shape[0]
    if not(is_connected(adj,n)):
        print('This graph is not connected. No spanning tree exists')
    else:
        tr=np.zeros((n,n))
        adj[adj==0]=-np.inf
        conn_nodes = [0]
        rem_nodes = [i+1 for i in range(n-1)]
        while len(rem_nodes)>0:
            L=np.zeros(n)
            L[conn_nodes]=1
            L=L.reshape(n,1)
            C=np.zeros(n)
            C[rem_nodes]=1
            C=C.reshape(1,n)
            B=L.dot(C)
            A=B*adj
            i=np.where(A==np.max(A))[0][0]
            j=np.where(A==np.max(A))[1][0]
            tr[i,j]=1
            tr[j,i]=1
            conn_nodes+=[j]
            rem_nodes.remove(j)
    return tr.astype(int)

def build_similarity_graph(X, var=1, eps=0, k=0):
#      Computes the similarity matrix for a given dataset of samples.
#
#  Input
#  X:
#      (n x m) matrix of m-dimensional samples
#  k and eps:
#      controls the main parameter of the graph, the number
#      of neighbours k for k-nn, and the threshold eps for epsilon graphs
#  var:
#      the sigma value for the exponential function, already squared
#
#
#  Output
#  W:
#      (n x n) dimensional matrix representing the adjacency matrix of the graph
#  similarities:
#      (n x n) dimensional matrix containing
#      all the similarities between all points (optional output)

  assert eps + k != 0, "Choose either epsilon graph or k-nn graph"

#################################################################
#  build full graph
#  similarities: (n x n) matrix with similarities between
#  all possible couples of points.
#  The similarity function is d(x,y)=exp(-||x-y||^2/var)
#################################################################
  # euclidean distance squared between points
  dists = sd.squareform(sd.pdist(X, "sqeuclidean"))
  similarities = np.exp(-dists.astype(float) / (2.*var))

#################################################################
#################################################################

  if eps:
#################################################################
#  compute an epsilon graph from the similarities               #
#  for each node x_i, an epsilon graph has weights              #
#  w_ij = d(x_i,x_j) when w_ij > eps, and 0 otherwise           #
#################################################################
    similarities[similarities < eps] = 0

    return similarities

#################################################################
#################################################################

  if k:
#################################################################
#  compute a k-nn graph from the similarities                   #
#  for each node x_i, a k-nn graph has weights                  #
#  w_ij = d(x_i,x_j) for the k closest nodes to x_i, and 0      #
#  for all the k-n remaining nodes                              #
#  Remember to remove self similarity and                       #
#  make the graph undirected                                    #
#################################################################
    sort = np.argsort(similarities)[:, ::-1]  # descending
    mask = sort[:, k + 1:]  # indices to mask
    for i, row in enumerate(mask): similarities[i, row] = 0
    np.fill_diagonal(similarities, 0)  # remove self similarity
    return (similarities + similarities.T) / 2.  # make the graph undirected

    return similarities

#################################################################
#################################################################

def build_laplacian(W, laplacian_normalization=""):
#  laplacian_normalization:
#      string selecting which version of the laplacian matrix to construct
#      either 'unn'normalized, 'sym'metric normalization
#      or 'rw' random-walk normalization

#################################################################
# build the laplacian                                           #
# L: (n x n) dimensional matrix representing                    #
#    the Laplacian of the graph                                 #
#################################################################
  degree = W.sum(1)
  if not laplacian_normalization:
    return np.diag(degree) - W
  elif laplacian_normalization == "sym":
    aux = np.diag(1 / np.sqrt(degree))
    return np.eye(*W.shape) - aux.dot(W.dot(aux))
  elif laplacian_normalization == "rw":
    return np.eye(*W.shape) - np.diag(1 / degree).dot(W)
  else: raise ValueError

#################################################################
#################################################################

def plot_edges_and_points(X,Y,W,title=''):
    colors=['go-','ro-','co-','ko-','yo-','mo-']
    n=len(X)
    G=nx.from_numpy_matrix(W)
    nx.draw_networkx_edges(G,X)
    for i in range(n):
        plt.plot(X[i,0],X[i,1],colors[int(Y[i])])
    plt.title(title)
    plt.axis('equal')

def plot_graph_matrix(X,Y,W):
    plt.figure()
    plt.clf()
    plt.subplot(1,2,1)
    plot_edges_and_points(X,Y,W)
    plt.subplot(1,2,2)
    plt.imshow(W, extent=[0, 1, 0, 1])
    plt.show()

def plot_clustering_result(X,Y,W,spectral_labels,kmeans_labels,normalized_switch=0):
    plt.figure()
    plt.clf()
    plt.subplot(1,3,1)
    plot_edges_and_points(X,Y,W,'ground truth')
    plt.subplot(1,3,2)
    if normalized_switch:
        plot_edges_and_points(X,spectral_labels,W,'unnormalized laplacian')
    else:
        plot_edges_and_points(X,spectral_labels,W,'spectral clustering')
    plt.subplot(1,3,3)
    if normalized_switch:
        plot_edges_and_points(X,kmeans_labels,W,'normalized laplacian')
    else:
        plot_edges_and_points(X,kmeans_labels,W,'k-means')
    plt.show()

def plot_the_bend(X, Y, W, spectral_labels, eigenvalues_sorted):
    plt.figure()
    plt.clf()
    plt.subplot(1,3,1)
    plot_edges_and_points(X,Y,W,'ground truth')

    plt.subplot(1,3,2)
    plot_edges_and_points(X,spectral_labels,W,'spectral clustering')

    plt.subplot(1,3,3)
    plt.plot(np.arange(0,len(eigenvalues_sorted),1),eigenvalues_sorted,'v:')
    plt.show()

def plot_classification(X, Y,labels,  var=1, eps=0, k=0):

    plt.figure()

    W = build_similarity_graph(X, var=var, eps=eps, k=k)

    plt.subplot(1, 2, 1)
    plot_edges_and_points(X, Y, W, 'ground truth')

    plt.subplot(1, 2, 2)
    plot_edges_and_points(X, labels, W, 'HFS')
    plt.show()

def label_noise(Y, alpha):

    ind=np.arange(len(Y))
    random.shuffle(ind)
    Y[ind[:alpha]] = 3-Y[ind[:alpha]]
    return Y

def plot_classification_comparison(X, Y,hard_labels, soft_labels,var=1, eps=0, k=0):

    plt.figure()

    W = build_similarity_graph(X, var=var, eps=eps, k=k)

    plt.subplot(1,3,1)
    plot_edges_and_points(X, Y, W, 'ground truth')

    plt.subplot(1,3,2)
    plot_edges_and_points(X, hard_labels, W, 'Hard-HFS')

    plt.subplot(1,3,3)
    plot_edges_and_points(X, soft_labels, W, 'Soft-HFS')
    plt.show()

import numpy,import scipy,import networkx


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

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

# Importing the dataset
dataset = pd.read_csv('Position_Salaries.csv')
X = dataset.iloc[:, 1:2].values
y = dataset.iloc[:, 2].values

# creating a linear model for comparison
from sklearn.linear_model import LinearRegression
lin_reg = LinearRegression()
lin_reg.fit(X,y)

from sklearn.preprocessing import PolynomialFeatures
poly_reg = PolynomialFeatures(degree = 3)
X_poly = poly_reg.fit_transform(X)

lin_reg2 = LinearRegression()
lin_reg2.fit(X_poly,y)

#visualize linear result
plt.scatter(X,y,color='red')
plt.plot(X, lin_reg.predict(X.reshape(-1,1)), color='blue')

X_grid = np.arange(min(X), max(X),step = 0.1)
X_grid = X_grid.reshape((len(X_grid), 1))
plt.scatter(X,y,color='red')
plt.plot(X_grid,lin_reg2.predict(poly_reg.fit_transform(X_grid)), color ='blue')

Machine Learning A-Z Template Folder/Part 2 - Regression/Section 6 - Polynomial Regression/j_polynomial_regression.py


import numpy as np
from sympy import *

def tx(x = 0):
  """
    Translation on «x» axis. Returns homogeneous transformation matrix
  """
  return np.array([[1, 0, 0, x],
                   [0, 1, 0, 0],
                   [0, 0, 1, 0],
                   [0, 0, 0, 1]])

def symbolicTx(x = 0):
  """
    Translation on «x» axis. Returns homogeneous transformation matrix
  """
  return Matrix([[1, 0, 0, x],
                 [0, 1, 0, 0],
                 [0, 0, 1, 0],
                 [0, 0, 0, 1]])

def ty(y = 0):
  '''
  Translation on «y» axis. Returns homogeneous transformation matrix
  '''
  return np.array([[1, 0, 0, 0],
                   [0, 1, 0, y],
                   [0, 0, 1, 0],
                   [0, 0, 0, 1]])

def symbolicTy(y = 0):
  """
    Translation on «y» axis. Returns homogeneous transformation matrix
  """
  return Matrix([[1, 0, 0, 0],
                 [0, 1, 0, y],
                 [0, 0, 1, 0],
                 [0, 0, 0, 1]])

def tz(z = 0):
  '''
  Translation on «z» axis. Returns homogeneous transformation matrix
  '''
  return np.array([[1, 0, 0, 0],
                   [0, 1, 0, 0],
                   [0, 0, 1, z],
                   [0, 0, 0, 1]])

def symbolicTz(z = 0):
  """
    Translation on «z» axis. Returns homogeneous transformation matrix
  """
  return Matrix([[1, 0, 0, 0],
                 [0, 1, 0, 0],
                 [0, 0, 1, z],
                 [0, 0, 0, 1]])

def rx(x = 0):
  '''
    Rotation on «x» axis. Returns homogeneous transformation matrix
  '''
  return np.array([[1, 0, 0, 0],
                   [0, np.cos(x), -np.sin(x), 0],
                   [0, np.sin(x), +np.cos(x), 0],
                   [0, 0, 0, 1]])

def symbolicRx(x = 0):
  """
    Rotation on «x» axis. Returns homogeneous transformation matrix
  """
  return Matrix([[1, 0, 0, 0],
                 [0, cos(x), -sin(x), 0],
                 [0, sin(x), +cos(x), 0],
                 [0, 0, 0, 1]])

def ry(y = 0):
  '''
    Rotation on «y» axis. Returns homogeneous transformation matrix
  '''
  return np.array([[np.cos(y), 0, +np.sin(y), 0],
                   [0, 1, 0, 0],
                   [-np.sin(y), 0, np.cos(y), 0],
                   [0, 0, 0, 1]])

def symbolicRy(y = 0):
  """
    Rotation on «y» axis. Returns homogeneous transformation matrix
  """
  return Matrix([[cos(y), 0, +sin(y), 0],
                 [0, 1, 0, 0],
                 [-sin(y), 0, cos(y), 0],
                 [0, 0, 0, 1]])

def rz(z = 0):
  '''
    Rotation on «z» axis. Returns homogeneous transformation matrix
  '''
  return np.array([[np.cos(z), -np.sin(z), 0, 0],
                   [np.sin(z), +np.cos(z), 0, 0],
                   [0, 0, 1, 0],
                   [0, 0, 0, 1]])

def symbolicRz(z = 0):
  """
    Rotation on «z» axis. Returns homogeneous transformation matrix
  """
  return Matrix([[cos(z), -sin(z), 0, 0],
                 [sin(z), +cos(z), 0, 0],
                 [0, 0, 1, 0],
                 [0, 0, 0, 1]])

def axisAngle(H):
  """
    This function computes the axis - angle vector «X» using the Homogeneous Transformation Matrix of a reference frame
    H: np.array (two - dimensional)
  """
  theta = np.arccos((np.trace(H[0 : 3, 0 : 3]) - 1)/2)
  n = (1/(2 * np.sin(theta))) * np.array([[H[2, 1] - H[1, 2]],
                                          [H[0, 2] - H[2, 0]],
                                          [H[1 ,0] - H[0, 1]]])
  x = np.append(H[0 : 3, 3], theta * n)
  return x.reshape((6, 1))

def symbolicAxisAngle(H):
  """
    This function computes the axis - angle vector «X» using the Homogeneous Transformation Matrix of a reference frame
    H: np.array (two - dimensional)
  """
  theta = simplify(acos((H[0 : 3, 0 : 3].trace() - 1) / 2))
  n = (1 / (2 * sin(theta))) * Matrix([[H[2, 1] - H[1, 2]],
                                       [H[0, 2] - H[2, 0]],
                                       [H[1 ,0] - H[0, 1]]])
  X = H[0 : 3, 3].row_insert(3, simplify(theta * n))
  return X


import numpy as np

def Bspline(i, k, u, node_vector):
    ''' 计算k次规范B样条基函数'''

    if k == 0:  # 0次B样条
        if node_vector[i] <= u < node_vector[i + 1]:
            Nik_u = 1.0
        else:
            Nik_u = 0.0

    else:
        length1 = node_vector[i + k] - node_vector[i]
        length2 = node_vector[i + k + 1] - node_vector[i + 1]
        if length1 == 0.0:   # 规定 0/0 = 0
            length1 = 1.0
        if length2 == 0.0:
            length2 = 1.0

        Nik_u_1 = (u - node_vector[i]) / length1 * \
            Bspline(i, k - 1, u, node_vector)
        Nik_u_2 = (node_vector[i + k + 1] - u) / \
            length2 * Bspline(i + 1, k - 1, u, node_vector)
        Nik_u = Nik_u_1 + Nik_u_2

    return Nik_u

def U_uniform(n, k):
    t = np.linspace(0, 1, n + k + 2, endpoint=True)
    return t

def U_quasi_uniform(n, k):
    '''
        准均匀B样条的节点向量计算, n+1个控制节点,k次B样条
    '''
    t = np.linspace(0, 1, n - k + 2, endpoint=True)  # 内结点 均匀分布
    t = np.append(np.zeros(k), t)  # 左边 k个结点 置0
    t = np.append(t, np.ones(k))  # 右边 k个结点 置1
    return t

def U_piecewise_Bezier(n, k):
    # 分段Bezier曲线的节点向量计算，共n+1个控制顶点，k次B样条
    # 分段Bezier端节点重复度为k+1，内间节点重复度为k,且满足n/k为正整数

    # 0...k  k+1...n+1  n+2...n+k+1
    # if (n % k) == 0 and (k % 1) == 0 and k >= 1:
    node_vector = np.zeros(n + k + 2)
    node_vector[n + 1: n + k + 2] = 1  # 右端节点置1
    # print(n, k)
    # print(node_vector)

    piecewise = int(n / k)
    flag = 0
    print(piecewise)
    if piecewise > 1:
        for i in range(piecewise - 1):  # 共 piecewise-1组
            for j in range(k):  # k 重复度
                node_vector[k + flag * k + j + 1] = (i + 1) / piecewise
            flag += 1
    else:
        print("error")
        # return None
    return node_vector

#-------------------- Bspline dots -----------------
def bspline(n, k, points, fns):
    if(fns == "uniform"):
        node_vector = U_uniform(n, k)  # n+k+2
    elif(fns == "quasi"):
        node_vector = U_quasi_uniform(n, k)
    elif(fns == "piecewise" and (n % k) == 0 and (k % 1) == 0 and k >= 1):
        node_vector = U_piecewise_Bezier(n, k)

    else:
        return None
    # n = t-k-1 =>　n+1?
    Nik = np.zeros((n + 1, 1))
    P_u_x = list()
    P_u_y = list()

    U = np.linspace(k / (n + k + 1), (n + 1) / (n + k + 1), 500,
                    endpoint=True) if fns == "uniform" else np.linspace(0, 1, 200, endpoint=False)

    for u in U:
        for i in range(0, n + 1, 1):
            Nik[i, :] = Bspline(i, k, u, node_vector)

        p_u = np.dot(points.T, Nik)  # 曲线上点的坐标
        P_u_x.append(p_u[0, 0])  # 所有点的横坐标
        P_u_y.append(p_u[1, 0])  # 所有点的纵坐标

    # P_u_x = np.asarray(P_u_x)
    # P_u_y = np.asarray(P_u_y)
    return P_u_x, P_u_y

def echo():
    return "from bspline echo"

def get_points(raw_points: list):
    # points = np.array([len(raw_points), 2])
    points = np.array([[raw_points[0]["x"], raw_points[0]["y"]]])
    for point in raw_points[1:]:
        points = np.vstack((points, np.array([point["x"], point["y"]])))

    return points


# Implementation taken from
# https://stackoverflow.com/questions/43238173/python-convert-matrix-to-positive-semi-definite/43244194#43244194

import numpy as np
import torch

from .is_pos_def import is_pos_def

def nearest_pd(A, f=np.spacing):
    """Find the nearest positive-definite matrix to input

    A Python/Numpy port of John D'Errico's `nearestSPD` MATLAB code [1], which credits [2]

    [1] https://www.mathworks.com/matlabcentral/fileexchange/42885-nearestspd

    [2] https://doi.org/10.1016/0024-3795(88)90223-6
    """

    B = (A + A.T) / 2
    _, s, V = torch.svd(B)

    # For a comparison with kanga, see the following:
    # https://github.com/pytorch/pytorch/issues/16076#issuecomment-477755364
    H = torch.matmul(V, torch.matmul(torch.diag(s), V.T))

    A2 = (B + H) / 2

    A3 = (A2 + A2.T) / 2

    if is_pos_def(A3):
        return A3

    spacing = f(torch.norm(A).item())
    I = torch.eye(A.shape[0])
    k = 1
    while not is_pos_def(A3):
        eigenvals = torch.eig(A3, eigenvectors=False)[0][:, 0]
        mineig = eigenvals.min().item()
        A3 += I * (-mineig * k**2 + spacing)
        k += 1

    return A3


"""
ackley.py by Adhisha Gammanpila (https://github.com/adhishagc/Ackley-Function)

-----------------------------------

MIT License

Copyright (c) 2019 Adhisha Gammanpila

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

"""

#importing libraries
import numpy as np

def ackley_function(x1,x2):
  #returns the point value of the given coordinate
  part_1 = -0.2*np.sqrt(0.5*(x1*x1 + x2*x2))
  part_2 = 0.5*(np.cos(2*np.pi*x1) + np.cos(2*np.pi*x2))
  value = np.exp(1) + 20 -20*np.exp(part_1) - np.exp(part_2)
  #returning the value
  return value


from sympy import Matrix, Float, Symbol
from BMSS.strike_goldd_simplified import*

###############################################################################
#Model
###############################################################################

x, s, h = [Symbol(x) for x in ['x', 's', 'h']]
mu_max, Ks, Y, synh, Kind = [Symbol(x) for x in ['mu_max', 'Ks', 'Y', 'synh', 'Kind']]
Ind = Symbol('Ind')

mu   = mu_max*s/(s+Ks)

dx  =  x*mu
ds  = -dx/Y
dh  =  synh*Ind/(Ind+Kind) -h*mu

variables = {'x': x, 's': s, 'h': h, 'mu_max': mu_max, 'Ks': Ks, 'Y': Y, 'synh': synh, 'Kind': Kind, 'Ind': Ind}

###############################################################################
#Specify Input for IA
###############################################################################
#User makes changes here
measured_states    = Matrix([x, s, h])
states             = Matrix([x, s, h])
unknown_parameters = Matrix([synh, Kind])
diff               = Matrix([dx, ds, dh])
input_conditions   = {Ind: 6}
init_conditions    = {x: Float(1, 3), s: Float(1, 3), h: Float(1, 3)}
decomposition      = [[x, s, h]
                      ]

known_parameters = {Y : 3, Ks : 0.15, mu_max : 0.012}
diff = diff.subs(known_parameters.items())

###############################################################################
#Call Strike-Goldd
###############################################################################
def run_strike_goldd():
    start_time = time()
    x_aug_dict = strike_goldd(measured_states,
                              states,
                              unknown_parameters,
                              input_conditions,
                              diff,
                              init_conditions,
                              decomposition)
    print("Total time: ",time()-start_time)
    print(x_aug_dict)
    return x_aug_dict

if __name__ == '__main__':
    pass


# coding: utf-8
'''
2. Further Analyzing the Wikiepedia voters network
'''

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

SOURCE_FILE = './data/wiki-Vote.txt'
FIT_DEGREE = 1

wikiGraph = snap.LoadEdgeList(snap.PNGraph, SOURCE_FILE, 0, 1)
assert 103689 == wikiGraph.GetEdges()
assert 7115 == wikiGraph.GetNodes()

# 2.1: Histogram of out-degree distribution.
# Calculate degree and construct data frame.
outDegreeToCount = snap.TIntPrV()
snap.GetOutDegCnt(wikiGraph, outDegreeToCount)
data = pd.DataFrame([[item.GetVal1(), item.GetVal2()]
                     for item in outDegreeToCount
                     if item.GetVal1() > 0 and item.GetVal2() > 0])
data.columns = ['degree', 'count']

# Plot.
fig = plt.plot(data['degree'], data['count'], 'bo--', markersize=2)[0]
fig.axes.set_xscale('log')
fig.axes.set_yscale('log')
fig.axes.set_xlim(data['degree'].min(), data['degree'].max())
fig.axes.set_ylim(data['count'].min(), data['count'].max())
fig.axes.set_title("Log-Log Degree Distribution Plot for WikiGraph")
fig.axes.set_xlabel("Node Degree")
fig.axes.set_ylabel("Node Count")

# Save image.
plt.savefig("WikiGraphOutDegreeDistribution", format='svg', dpi=600)
plt.savefig("WikiGraphOutDegreeDistribution", dpi=600)

# Alternative 2.1.
snap.PlotOutDegDistr(wikiGraph, "WikiGraph",
                     "WikiGraph - Out Degree Distribution")

# 2.2: Compute and plot the least-square regression line.
# Calculate the best fit line on the log data.
slope, intercept = np.polyfit(
    np.log10(data['degree']), np.log10(data['count']), FIT_DEGREE)
predict = lambda x: 10**(intercept)*x**slope

# Plot.
fig = plt.plot(data['degree'], data['count'], 'bo--',
               data['degree'], predict(data['degree']), 'g',
               markersize=2)[0]
fig.axes.set_xscale('log')
fig.axes.set_yscale('log')
fig.axes.set_xlim(data['degree'].min(), data['degree'].max())
fig.axes.set_ylim(data['count'].min(), data['count'].max())
fig.axes.set_title("Log-Log Degree Distribution Plot And Fit for WikiGraph")
fig.axes.set_xlabel("Node Degree")
fig.axes.set_ylabel("Node Count")

# Save image.
plt.savefig("WikiGraphOutDegreeDistributionFit", format='svg', dpi=600)
plt.savefig("WikiGraphOutDegreeDistributionFit", dpi=600)

# Print the results.
print("The best fit line is given by equation "
      "log(y) = %s * log(x) + %s" % (slope, intercept))

# Alternative 2.1.
snap.PlotOutDegDistr(wikiGraph, "WikiGraphFit",
                     "WikiGraph - Out Degree Distribution with Fit",
                     False, True)
print("The best fit line is given by equation "
      "log(y) = -1.411 * log(x) + 3.3583 with R^2=0.91")


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

###
# Name: Nikki and Riley
# Student ID: 2267883 2274503
# Email: schwa218@mail.chapman.edu keda106@mail.chapman.edu
# Course: PHYS220/MATH220/CPSC220 Fall 2017
# Assignment: Classwork 4
###

"""Classwork 04
This classwork introduces numpy arrays and compares their performance to
python lists.
"""

import math
import numpy as np

#GAUSSIAN LIST

def gen_gaussian_list(a, b, n=1000):
    """gen_gaussian_list(a, b, n=1000)
    Generate a discrete approximation of a Gaussian function, including its
    domain and range, stored as a pair of vanilla python lists.

    Args:
        a (float) : Lower bound of domain
        b (float) : Upper bound of domain
        n (int, optional) : Number of points in domain, defaults to 1000.

    Returns:
        (x, g) : Pair of lists of floats
            x  : [a, ..., b] List of n equally spaced floats between a and b
            g  : [g(a), ..., g(b)] List of Gaussian values matched to x
    """
    dx = (b-a)/(n-1)                         # spacing between points
    x = [a + k*dx for k in range(n)]         # domain list

    # Local implementation of a Gaussian function
    def gauss(x):
        return (1/math.sqrt(2*math.pi))*math.exp(-x**2/2)

    g = [gauss(xk) for xk in x]                  # range list
    return (x, g)

#NEW GAUSS ARRAY USING NUMPY

def gen_gaussian_array(a, b, n=1000):
    """gen_gaussian_array(a, b, n=1000)
    Generate a discrete approximation of a Gaussian function, including its
    domain and range, stored as a pair of numpy arrays.

    Args:
        a (float) : Lower bound of domain
        b (float) : Upper bound of domain
        n (int, optional) : Number of points in domain, defaults to 1000.

    Returns:
        (x, g) : Pair of numpy arrays of float64
            x  : [a, ..., b] Array of n equally spaced float64 between a and b
            g  : [g(a), ..., g(b)] Array of Gaussian values matched to x
    """
    ### No need for np.array here. np.linspace already returns an array.
    x = np.array(np.linspace(a,b,n),dtype=np.float64)
    def gauss(x):
        return (1/math.sqrt(2*math.pi))*math.exp(-x**2/2)  ### Do not use math if using numpy. Use np.sqrt etc.
    ### Vectorize is not needed here since all operations are already vectorized (if np.exp and np.pi and np.sqrt are used)
    fx = np.vectorize(gauss)
    g = fx(x)  ### No need to redefine g here. Just return gauss(x) directly.
    return(x,g)

#SINC LIST

def sinc_list(a, b, n=10000):
    """sinc_list(a, b, n=10000)
    Generate a discrete approximation of a Sinc function, including its
    domain and range, stored as a pair of vanilla python lists.

    Args:
        a (float) : Lower bound of domain
        b (float) : Upper bound of domain
        n (int, optional) : Number of points in domain, defaults to 10000.

    Returns:
        (x, g) : Pair of lists of floats
            x  : [a, ..., b] List of n equally spaced floats between a and b
            g  : [g(a), ..., g(b)] List of Gaussian values matched to x
    """
    dx = (b-a)/(n-1)                         # spacing between points
    x = [a + k*dx for k in range(n)]         # domain list

    # Local implementation of a Sinc function
    def sinc(x):
        if x == 0:
           return 1
        else:
            return (math.sin(x)/x)

    g = [sinc(xk) for xk in x]                  # range list
    return (x, g)

#NEW SINC ARRAY USING NUMPY

def sinc_array(a, b, n=10000):
    """sinc_array(a, b, n=10000)
    Generate a discrete approximation of a Sinc function, including its
    domain and range, stored as a pair of numpy arrays.

    Args:
        a (float) : Lower bound of domain
        b (float) : Upper bound of domain
        n (int, optional) : Number of points in domain, defaults to 10000.

    Returns:
        (x, g) : Pair of numpy arrays of float64
            x  : [a, ..., b] Array of n equally spaced float64 between a and b
            g  : [g(a), ..., g(b)] Array of Gaussian values matched to x
    """
    x = np.array(np.linspace(a,b,n),dtype=np.float64)  ### no need for np.array
    def sinc(x):
        if x == 0:
            return 1
        else:
            return (math.sin(x)/x)
    fx = np.vectorize(sinc)  ### Vectorize IS needed here, since the if statement in sinc is not vectorized yet.
    g = fx(x)  ### no need for g, just return fx(x) directly.
    return(x,g)

def main(a,b,n=1000):
    """main(a, b, n=1000)
    Main function for command line operation. Prints result of Gaussian to screen.

    Args:
        a (float) : Lower bound of domain
        b (float) : Upper bound of domain
        n (int, optional) : Number of points in domain, defaults to 1000.

    Returns:
        None

    Effects:
        Prints Gaussian to screen.
    """
    # You can unpack tuples as return values easily
    x, g = gen_gaussian_list(a,b,n)
    # The zip function takes two lists and generates a list of matched pairs
    for (xk, gk) in zip(x, g):
        # The format command replaces each {} with the value of a variable
        print("({}, {})".format(xk, gk))

# Protected main block for command line operation
if __name__ == "__main__":
    # The sys module contains features for running programs
    import sys
    # The sys.argv list variable contains all command line arguments
    #    sys.argv[0] is the program name always
    #    sys.argv[1] is the first command line argument, etc
    if len(sys.argv) == 4:
        a = float(sys.argv[1])
        b = float(sys.argv[2])
        n = int(sys.argv[3])
        main(a,b,n)
    elif len(sys.argv) == 3:
        a = float(sys.argv[1])
        b = float(sys.argv[2])
        main(a,b)
    else:
        print("Usage: cw04.py a b [n]")
        print("  a : float, lower bound of domain")
        print("  b : float, upper bound of domain")
        print("  n : integer, number of points in domain")
        exit(1)


import numpy as np
import math

from corai_util.tools import function_iterable

# defines the coefficients for fractional ADAMS method in order to compute a SDE path.
# it needs the number of coefficients as well as the alpha of roughness.
def fractional_ADAMS(k, alpha, DELTA):
    # a needs k+2 elements, because j \in 0,k+1.
    # b doesn't start at 0, so only k+1 elements.
    a = np.zeros(k + 2)
    b = np.zeros(k + 1)
    for i in range(k + 2):
        if i == 0:
            a[i] = k ** (alpha + 1) - (k - alpha) * (k + 1) ** alpha
        if i == k + 1:
            a[i] = 1
        if (i != k + 1) and (i != 0):
            a[i] = (k - i + 2) ** (alpha + 1) + (k - i) ** (alpha + 1) - 2 * (k - i + 1) ** (alpha + 1)
        if i != k + 1:
            b[i] = (k + 1 - i) ** alpha - (k - i) ** alpha
    a = 1 / math.gamma(alpha + 2) * DELTA ** alpha * a
    b = 1 / math.gamma(alpha + 1) * DELTA ** alpha * b
    return a, b

def system_ODE_solver(tt, starting_point, function_evolution, left_or_right="left"):
    """
    Semantics:
        ODE solver with Euler's method. It solves liner systems of ODEs of the first oder.
        left to right flow.

    Args:
        tt: grid of times. Assume regularity for the grid. The same grid for all functions.
        starting_point: LIST of lower boundary, starting condition
                        time and previous points for each function.
                        Same order as in function_evolution.
        function_evolution: LIST of "1 + len(function_evolution)" dimensional function.
        left_or_right: starting point on left or right. Changes the equations, either backwards of forwards equations.

    Notes:
        function_evolution and starting_point as lists type for optimization of the code
        (access constant and small overhead)

    Returns:
        A list with the values of the function, over the grid tt. format: [[x1,y1...], [x2,y2...] ... ]

    """
    assert function_iterable.is_iterable(function_evolution), "Function Evolution is not iterable."
    assert function_iterable.is_iterable(starting_point), "Starting Point is not iterable."

    L = len(tt)
    J = len(function_evolution)
    yy = [[0] * J for _ in range(L)]  # creates a L by J matrix.
    # List comprehension in order to avoid copy of mutable element

    if left_or_right == "left":
        yy[0] = starting_point
        a = 0  # beg range
        b = L - 1  # end range; skipping first step
        step = 1  # how increment
        DELTA = tt[1] - tt[0]

    else:
        yy[-1] = starting_point
        a = L - 1  # beg range
        b = 0  # end range; skipping first step
        step = -1  # how increment
        DELTA = tt[0] - tt[1]  # delta changes because of the direction

    for i in range(a, b, step):
        for j in range(J):
            yy[i + step][j] = yy[i][j] + DELTA * function_evolution[j](tt[i], *yy[i])
    return yy

"""

def support(sigma1, sigma2):  # gives back when the normal given by sigma 1 is lower than the normal given by sigma2
    xm = np.sqrt(2) * sigma1 * sigma2 * np.sqrt(np.log(sigma1 / sigma2) / (sigma1 - sigma2)) / np.sqrt(sigma1 + sigma2)
    return -xm, xm  # by symmetry

################ DATA:
SIGMA = [1., 2.2]
tm, tM = support(SIGMA[0], SIGMA[1])
tm *= 0.95
tM *= 0.95
# times 0.9 so one can be inside the interval, all the time.
# Otherwise we face boundary issues.
NB_GRID_POINT = int(1E4)  # conversion float to int for linspace.
tt = np.linspace(tm, tM, NB_GRID_POINT)

density_mu = lambda tt: norm.pdf(tt, 0, SIGMA[0])  # probably not the fastest choice for single evaluation, but great for vectorization !
density_nu = lambda tt: norm.pdf(tt, 0, SIGMA[1])  # probably not the fastest choice for single evaluation, but great for vectorization !

# for x \ihow_much_rotate E !
density_eta = lambda tt: np.maximum(density_mu(tt) - density_nu(tt), 0)
density_gamma = lambda tt: np.maximum(density_nu(tt) - density_mu(tt), 0)

# FIRST PLOT:
plt.figure(figsize=(10, 4))
plt.plot(tt, density_mu(tt), 'b', label=r'$\mu$')
plt.plot(tt, density_nu(tt), 'k', label=r'$\nu$')
plt.plot(tt, density_eta(tt), 'r', label=r'$\eta$')
plt.plot(tt, density_gamma(tt), 'g', label=r'$\gamma$')
plt.title("The support of E is (%.3f,%.3f)" % (support(SIGMA[0], SIGMA[1])))
plt.legend(loc="best")

# SOLVING ODE FROM LEFT !!!
func_p = lambda t, x, y: (t - y) / (y - x) * density_eta(t) / density_gamma(x)
func_q = lambda t, x, y: (x - t) / (y - x) * density_eta(t) / density_gamma(y)
STARTING_POINT = [tm / 0.95 * 1.05,  # tm /0.95 is equal to the true tm (we narrowed the interval for simulation)
                  # and *1.05 so we are not at the boundary.
                  8.]  # p(a), q(a), remember p < q. For that reason we take q(a) large (\approx to inf).

my_nested_results = system_ODE_solver(tt, STARTING_POINT, [func_p, func_q], "left")

"""


import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# %matplotlib inline
plt.style.use("fivethirtyeight")

df = pd.read_csv(r'C:\Users\mikol\Desktop\Python_Met_II/Fish.csv')
data = df.copy()
data.head()
data["Species"].value_counts(normalize=True)
data = data.loc[data["Species"] == "Roach"]["Length1"]
pd.DataFrame({'values': data.describe()}).reset_index()

print(f'Mean: {data.mean():.2f}')
conf_i = np.percentile(data, [2.5, 97.5])
print(f'Confidence intervals: {conf_i}')

sns.set()
plt.hist(data, bins=15)
plt.xlabel("Lenght1")
plt.ylabel("Number of Roach fish")
plt.savefig("hist1.png")
plt.show()

x = np.sort(data)
n = len(data)
y = np.arange(1, n+1)/n
plt.plot(x,y, marker=".", linestyle="none")
plt.margins(0.02)
plt.show()

ax = sns.distplot(data,bins=10,kde=True,color='skyblue')
ax.axvline(conf_i[0])
ax.axvline(conf_i[1])
ax.text(11,0.12, conf_i[0])
ax.text(28,0.12, round(conf_i[1],2))
ax.set(xlabel='Distribution of Length1 Values', ylabel='Frequency')
ax.set_title("Before Bootstrapping", fontsize=20)
plt.savefig("fish_ci_before.png")
plt.show();

mean_lengths, n = [], 1000
for i in range(n):
    sample = np.random.choice(data,
                              replace=True,
                              size=len(data))
    sample_mean = sample.mean()
    mean_lengths.append(sample_mean)

# Calculate bootstrapped mean and 95% confidence interval.
boot_mean = np.mean(mean_lengths)
boot_ci = np.percentile(mean_lengths, [2.5, 97.5])
print("Bootstrapped Mean Length = {}, 95% CI = {}".format(boot_mean, boot_ci))

ax = sns.distplot(mean_lengths,bins=10,kde=True,color='skyblue')
ax.axvline(boot_ci[0])
ax.axvline(boot_ci[1])
ax.text(18.2,0.45, round(boot_ci[0], 2))
ax.text(22.3,0.45, round(boot_ci[1], 2))
ax.set_title("After Bootstrapping", fontsize=20)
ax.set(xlabel='Distribution of Length1 Values', ylabel='Frequency')
plt.savefig("fish_ci_after.png")
plt.show();

data = df.copy()
data = data.loc[data["Species"] == "Roach"][["Length1", "Weight"]]
data.sample(2)

data.corr()

data_size, lw_corr = data.shape[0], []
for i in range(1000):
    tmp_df = data.sample(n=data_size, replace=True)
    lw_corr.append(tmp_df["Weight"].corr(tmp_df["Length1"]))

corr_ci = np.percentile(lw_corr, [2.5, 97.5])
print("Correlation 95% Confidence Interval between Weight and Length1 = {}".format(corr_ci))

data = df.copy()
data = data.loc[data["Species"] == "Roach"]["Length1"]

mean_lengths, n = [], len(data)
index = np.arange(n)

for i in range(n):
    jk_sample = data[index != i]
    mean_lengths.append(jk_sample.mean())

mean_lengths_jk = np.mean(np.array(mean_lengths))
jk_variance = (n-1)*np.var(mean_lengths)
print("Jackknife estimate of the mean = {}".format(mean_lengths_jk))
print("Jackknife estimate of the variance = {}".format(jk_variance))

data = df.copy()
data = data.loc[data["Species"] == "Roach"][["Length1", "Length2"]]
data.head()

print(f'Mean of Length1: {data["Length1"].mean():.2f}')
print(f'Mean of Length2: {data["Length2"].mean():.2f}')

sample1 = data["Length1"]
sample2 = data["Length2"]

data = np.concatenate([sample1, sample2])

perm = np.array([np.random.permutation(len(sample1) + len(sample2)) for i in range(10000)])
permuted_1_db = data[perm[:, :len(sample1)]]
permuted_2_db = data[perm[:, len(sample1):]]

samples = np.mean(permuted_1_db, axis=1) - np.mean(permuted_2_db, axis=1)

test_stat = np.mean(sample1) - np.mean(sample2)
p_val = 2*np.sum(samples >= np.abs(test_stat))/10000
print("test_statistic = {}".format(test_stat))
print("p-value = {}".format(p_val))


# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
import numpy as np

def get_r2(y_true, y_pred):
    """R2 or the error.

    Parameters
    ----------
    y_true : numpy.ndarray or list
        Ground truth (correct) values

    y_pred : numpy.ndarray or list
        Predicted values, as returned by a regression estimator.

    Returns
    -------
    loss : float
        R2 value.
    """
    SStot = np.sum((y_true-y_true.mean())**2)
    SSres = np.sum((y_true-y_pred)**2)
    return 1 - SSres/SStot

def get_rmse(y_true, y_pred):
    """Compute maximum absolute error.

    Parameters
    ----------
    y_true : numpy.ndarray or list
        Ground truth (correct) values.

    y_pred : numpy.ndarray or list
        Predicted values, as returned by a regression estimator.

    Returns
    -------
    loss : float
        The maximum absolute error times the sign of the error.
    """
    SSres = np.mean((y_true-y_pred)**2)
    return SSres**0.5

def get_max_error(y_true, y_pred):
    """Compute maximum absolute error.

    Parameters
    ----------
    y_true : numpy.ndarray or list
        Ground truth (correct) values.

    y_pred : numpy.ndarray or list
        Predicted values, as returned by a regression estimator.

    Returns
    -------
    loss : float
        The maximum absolute error.
    """
    return np.array(y_pred-y_true)[np.argmax(np.abs(y_pred-y_true))]


import numpy as np

def exMax(flips, theta_A=None, theta_B=None, maxiter=10):
    # Initial Guess
    theta_A = theta_A or random.random()
    theta_B = theta_B or random.random()
    thetas = [(theta_A, theta_B)]
    # Iterate
    for c in range(maxiter):
        print("#%d:\t%0.2f %0.2f" % (c, theta_A, theta_B))
        heads_A, tails_A, heads_B, tails_B = e_step(flips, theta_A, theta_B)
        theta_A, theta_B = m_step(heads_A, tails_A, heads_B, tails_B)

    thetas.append((theta_A, theta_B))
    return thetas, (theta_A, theta_B)

"""Produce the expected value for heads_A, tails_A, heads_B, tails_B
    over the rolls given the coin biases"""
def e_step(rolls, theta_A, theta_B):

    heads_A, tails_A = 0, 0
    heads_B, tails_B = 0, 0
    for trial in rolls:
        likelihood_A = coin_likelihood(trial, theta_A)
        likelihood_B = coin_likelihood(trial, theta_B)
        p_A = likelihood_A / (likelihood_A + likelihood_B)
        p_B = likelihood_B / (likelihood_A + likelihood_B)
        heads_A += p_A * trial.count("H")
        tails_A += p_A * trial.count("T")
        heads_B += p_B * trial.count("H")
        tails_B += p_B * trial.count("T")
    return heads_A, tails_A, heads_B, tails_B

"""Produce the values for theta that maximize the expected number of heads/tails"""
def m_step(heads_A, tails_A, heads_B, tails_B):

    # Replace dummy values with your implementation
    theta_A = heads_A / (heads_A + tails_A)
    theta_B = heads_B / (heads_B + tails_B)
    return theta_A, theta_B

def coin_likelihood(roll, bias):
    # P(X | Z, theta)
    numHeads = roll.count("H")
    flips = len(roll)
    return pow(bias, numHeads) * pow(1 - bias, flips - numHeads)

flips = [ "HTTTHHTHTH", "HHHHTHHHHH", "HTHHHHHTHH",
          "HTHTTTHHTT", "THHHTHHHTH" ]
thetas, _ = exMax(flips, 0.6, 0.5, maxiter=6)