#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Jul 21 12:04:44 2020

Here are stored functions which return derivatives of certain values
things like Jacobians, Hessians, Flows and Nablas are defined here

import statement for ease of reference:
from kep_derivative_functions import nabla_lin_y,nabla_lin_x,nabla_l,nabla_q,nabla_H
from kep_derivative_functions import partial_Hqqq,partial_Hpp,partial_Hqq,hessian_H
from kep_derivative_functions import gradient,modified_gradient_en_ang_attractor,modified_gradient_energy_attractor

@author: steve
"""

import numpy as np
import math
from kep_util_globvar import G,M1,M2,ETA,ENERGY_0,TOTAL_ANG_MOMENTUM_0
from kep_util_globvar import normsq,supervec_norm
from kep_util_globvar import get_energy,get_total_angular_momentum

# %% GRADIANT FUNCTIONS, GRADIENTS OF FIRST INTEGRALS (NABLA)

# returns y' = J^{-1}\nabla H given y
# y is [p1,p2,q1,q2]
def gradient(y):
    # assume 2 body problem
    # here gradient is a bit of misnomer, it's the J^{-1}\nabla H that I label grad y
    r = np.array([y[2][0]-y[3][0] , y[2][1]-y[3][1]])#r from 1 to 0
    rabs = np.sqrt(normsq(r))
    grady = []
    grady.append(-r*G*M1*M2*(rabs**(-3)))
    grady.append(r*G*M1*M2*(rabs**(-3)))
    grady.append(y[0]/M1)
    grady.append(y[1]/M2)
    return np.array(grady)

# uses ENERGY_0 turns the energy manifold into an attractor
def modified_gradient_energy_attractor(y):
    r = np.array([y[2][0]-y[3][0] , y[2][1]-y[3][1]])#r from 1 to 0
    rabs = np.sqrt(normsq(r))
    grady = []
    grady.append(-r*G*M1*M2*(rabs**(-3)))
    grady.append(r*G*M1*M2*(rabs**(-3)))
    grady.append(y[0]/M1)
    grady.append(y[1]/M2)
    grady = np.array(grady)
    # an attracive term is added along nabla H
    energy_y = get_energy(y)
    attracting_term = nabla_H(y)
    # attracting_term = attracting_term / supervec_norm(attracting_term)
    attracting_term *= (ENERGY_0 - energy_y) * math.exp(ENERGY_0 - energy_y) /5

    return grady + attracting_term

# additionall attracting term to the ENERGY_0 and ANGULAR_M_0 manifolds
def modified_gradient_en_ang_attractor(y):
    r = np.array([y[2][0]-y[3][0] , y[2][1]-y[3][1]])#r from 1 to 0
    rabs = np.sqrt(normsq(r))
    grady = []
    grady.append(-r*G*M1*M2*(rabs**(-3)))
    grady.append(r*G*M1*M2*(rabs**(-3)))
    grady.append(y[0]/M1)
    grady.append(y[1]/M2)
    grady = np.array(grady)
    n_grady = math.sqrt(supervec_norm(grady))
    # an attractive term as in the naive approach, first energy
    energy_y = get_energy(y)
    energy_attracting_term = nabla_H(y)
    energy_attracting_term *= (ENERGY_0 - energy_y) * math.exp(ENERGY_0 - energy_y)
    e_att_n = np.sqrt(supervec_norm(energy_attracting_term))
    # make the attracting term smaller than the initial term (1/10th at most)
    if e_att_n * 10 > n_grady:
        energy_attracting_term *= n_grady / (10 * e_att_n)
    # then angular momentum
    ang_y = get_total_angular_momentum(y)
    ang_attracting_term = nabla_l(y)
    ang_attracting_term *= (TOTAL_ANG_MOMENTUM_0 - ang_y) * math.exp(TOTAL_ANG_MOMENTUM_0 - ang_y)
    ang_att_n = np.sqrt(supervec_norm(energy_attracting_term))
    # make the attracting term smaller if it's too big
    if ang_att_n * 10 > n_grady:
        ang_attracting_term *= n_grady / (10 * e_att_n)

    return grady + energy_attracting_term + ang_attracting_term

# returns the gradient of the hamiltonian
def nabla_H(y):
    r = np.array([y[2][0]-y[3][0] , y[2][1]-y[3][1]])# r from 1 to 0
    rabs = np.sqrt(normsq(r))
    nabla_h = []
    nabla_h.append(y[0]/M1)
    nabla_h.append(y[1]/M2)
    nabla_h.append(r*G*M1*M2*(rabs**(-3)))
    nabla_h.append(-r*G*M1*M2*(rabs**(-3)))
    return np.array(nabla_h)

# returns the hessian matrix of the hamiltonian at y
def hessian_H(y):
    # see section 3.1 kepler problem in report write-up
    # compute xi1 and xi2
    xi = math.sqrt(np.dot(y[2]-y[3],y[2]-y[3]))
    xito3 = xi**3
    xito5 = xi**5
    xi1 = y[3][0] - y[2][0]
    xi2 = y[3][1] - y[2][1]
    # we define each column manually (also symmetric so columns = rows), it's only eight by eight
    col1 = (1/M1,0,0,0,0,0,0,0)
    col2 = (0,1/M1,0,0,0,0,0,0)
    col3 = (0,0,1/M2,0,0,0,0,0)
    col4 = (0,0,0,1/M2,0,0,0,0)
    col5 = (0,0,0,0, -ETA/xito3 + ETA/xito5*xi1**2 , ETA/xito5*xi1*xi2 , ETA/xito3 - ETA/xito5*xi1**2 , -ETA/xito5*xi1*xi2)
    col6 = (0,0,0,0, ETA/xito5*xi1*xi2 , -ETA/xito3 + ETA/xito5*xi2**2 , -ETA/xito5*xi1*xi2 , ETA/xito3 - ETA/xito5*xi2**2)
    col7 = (0,0,0,0, ETA/xito3 - ETA/xito5*xi1**2 , -ETA/xito5*xi1*xi2 , -ETA/xito3 + ETA/xito5*xi1**2 , ETA/xito5*xi1*xi2)
    col8 = (0,0,0,0, -ETA/xito5*xi1*xi2 , ETA/xito3 - ETA/xito5*xi2**2 , ETA/xito5*xi1*xi2 , -ETA/xito3 + ETA/xito5*xi2**2)
    return np.array((col1,col2,col3,col4,col5,col6,col7,col8))

# returns 4x4 matrix, lower right hand block of hessian
def partial_Hqq(y):
    # see section 3.1 kepler problem in report write-up
    # compute xi1 and xi2
    xi = math.sqrt(np.dot(y[2]-y[3],y[2]-y[3]))
    xito3 = xi**3
    xito5 = xi**5
    xi1 = y[3][0] - y[2][0]
    xi2 = y[3][1] - y[2][1]
    # we define each column manually (also symmetric so columns = rows), it's only four by four
    col1 = (-ETA/xito3 + ETA/xito5*xi1**2 , ETA/xito5*xi1*xi2 , ETA/xito3 - ETA/xito5*xi1**2 , -ETA/xito5*xi1*xi2)
    col2 = (ETA/xito5*xi1*xi2 , -ETA/xito3 + ETA/xito5*xi2**2 , -ETA/xito5*xi1*xi2 , ETA/xito3 - ETA/xito5*xi2**2)
    col3 = (ETA/xito3 - ETA/xito5*xi1**2 , -ETA/xito5*xi1*xi2 , -ETA/xito3 + ETA/xito5*xi1**2 , ETA/xito5*xi1*xi2)
    col4 = (-ETA/xito5*xi1*xi2 , ETA/xito3 - ETA/xito5*xi2**2 , ETA/xito5*xi1*xi2 , -ETA/xito3 + ETA/xito5*xi2**2)
    return np.array((col1,col2,col3,col4))

# returns 4x4 matrix, upper right hand block of hessian
def partial_Hpp(y):
    col1 = (1/M1,0,0,0)
    col2 = (0,1/M1,0,0)
    col3 = (0,0,1/M2,0)
    col4 = (0,0,0,1/M2)
    return np.array((col1,col2,col3,col4))

def hessian_l(y):
    col1 = (0,0,0,0,0,-1,0,0)
    col2 = (0,0,0,0, 1,0,0,0)
    col3 = (0,0,0,0,0,0,0,-1)
    col4 = (0,0,0,0,0,0, 1,0)
    col5 = (0, 1,0,0,0,0,0,0)
    col6 = (-1,0,0,0,0,0,0,0)
    col7 = (0,0,0, 1,0,0,0,0)
    col8 = (0,0,-1,0,0,0,0,0)
    return np.array((col1,col2,col3,col4,col5,col6,col7,col8))

# returns 4x4x4 tensor
def partial_Hqqq(y):
    return

# takes the gradient of hamiltonian only wrt q1 and q2
def nabla_q(q):
    # assume 2bp
    # takes q returns it's gradient, which is negative the derivative of p
    r = np.array([q[0][0]-q[1][0] , q[0][1]-q[1][1]])#r from 1 to 0
    rabs = np.sqrt(normsq(r))
    nablaq = []
    nablaq.append(-r*G*M1*M2*(rabs**(-3)))# vector
    nablaq.append(r*G*M1*M2*(rabs**(-3)))
    return nablaq

# i think this one is more correct - angular momentum
def nabla_l(y):
    p1,p2,q1,q2 = y[0],y[1],y[2],y[3]
    nablal = []
    nablal.append(np.array([ -q1[1] , q1[0]])) # partial p1
    nablal.append(np.array([ -q2[1] , q2[0]]))
    nablal.append(np.array([ p1[1] , -p1[0]]))
    nablal.append(np.array([ p2[1] , -p2[0]]))
    return np.array(nablal)

# gradient of the total linear momentum in the x direction
# the level sets of this first integral are hyper-planes, so quite easy to figure out
def nabla_lin_x(y):
    return np.array([np.array([1,0]),np.array([1,0]),np.array([0,0]),np.array([0,0])])

# gradient of the total linear momentum in the y direction
def nabla_lin_y(y):
    return np.array([np.array([0,1]),np.array([0,1]),np.array([0,0]),np.array([0,0])])


r"""
Solve Poisson equation in 2D with periodic bcs in one direction
and homogeneous Neumann in the other

    \nabla^2 u = f,

Use Fourier basis for the periodic direction and Shen's Neumann basis for the
non-periodic direction.

The equation to solve is

     (\nabla^2 u, v) = (f, v)

"""
import sys
import os
import importlib
from sympy import symbols, cos, sin
import numpy as np
from shenfun import inner, div, grad, TestFunction, TrialFunction, \
    TensorProductSpace, FunctionSpace, Array, Function, comm, la

# Collect basis and solver from either Chebyshev or Legendre submodules
assert len(sys.argv) == 3, "Call with two command-line arguments"
assert sys.argv[-1].lower() in ('legendre', 'chebyshev')
assert isinstance(int(sys.argv[-2]), int)

family = sys.argv[-1].lower()
base = importlib.import_module('.'.join(('shenfun', family)))
Solver = base.la.Helmholtz

# Use sympy to compute a rhs, given an analytical solution
x, y = symbols("x,y", real=True)
ue = (1-x**3)*cos(2*y)
fe = ue-ue.diff(x, 2)-ue.diff(y, 2)

# Size of discretization
N = int(sys.argv[-2])
N = (N, N)
bc = {'left': ('N', ue.diff(x, 1).subs(x, -1)), 'right': ('N', ue.diff(x, 1).subs(x, 1))}
SN = FunctionSpace(N[0], family=family, bc=bc, mean=None)
K1 = FunctionSpace(N[1], family='F', dtype='d')
T = TensorProductSpace(comm, (SN, K1), axes=(0, 1))
u = TrialFunction(T)
v = TestFunction(T)

# Get f on quad points
fj = Array(T, buffer=fe)

# Compute right hand side of Poisson equation
f_hat = inner(v, fj)

# Get left hand side of Poisson equation
matrices = inner(v, u-div(grad(u)))

# Create Helmholtz linear algebra solver
H = Solver(*matrices)

# Solve and transform to real space
u_hat = Function(T).set_boundary_dofs()           # Solution spectral space
u_hat = H(u_hat, f_hat)       # Solve
uq = T.backward(u_hat).copy()

# Compare with analytical solution
uj = Array(T, buffer=ue)
print(abs(uj-uq).max())
assert np.allclose(uj, uq)
c = H.matvec(u_hat, Function(T))
f_hat = inner(v, fj)
assert np.allclose(c, f_hat)

if 'pytest' not in os.environ:
    import matplotlib.pyplot as plt
    plt.figure()
    X = T.local_mesh(True) # With broadcasting=True the shape of X is local_shape, even though the number of datapoints are still the same as in 1D
    plt.contourf(X[0], X[1], uq)
    plt.colorbar()

    plt.figure()
    plt.contourf(X[0], X[1], uj)
    plt.colorbar()

    plt.figure()
    plt.contourf(X[0], X[1], uq-uj)
    plt.colorbar()
    plt.title('Error')

    plt.show()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug  4 11:27:01 2021

@author: q

GOAL : Dev. Random Walk & LogNormal Price PDF

"""
# =============================================================================
# imports
# =============================================================================
# scientific computing
import numpy as np
# data handling
import pandas as pd
# visualization
import matplotlib.pyplot as plt

# =============================================================================
# functions
# =============================================================================

# =============================================================================
# program test
# =============================================================================

if __name__ == '__main__':

    """ Random Walk Modelling """
    def randomWalk(mu = 0, sigma = 1, periods = 252, n_paths = 1e4):
        """
        Random Walk Algorithm:
            - generate values with mu and sigma
            - cumsum each path
            - define each rows a rw period, and colums each path

        Returns :
            - pd.DataFrame :
                nrows = periods
                ncols = n_paths
                cumsum(axis = 0)
        """
        n_paths = int(n_paths)
        dt = 1 / periods
        return pd.DataFrame(np.random.normal(loc = mu * dt, scale = sigma * np.sqrt(dt), size = (periods, n_paths))).cumsum(axis = 0)

    kwargs = {'mu' : 0.06, 'sigma' : 0.4,
              'periods' : 252, 'n_paths' : 11 }

    """ Simulate Random Walks """
    df = randomWalk(**kwargs)
    # df.plot(legend = False)

    def logNormalReturnPaths(mu = 0, sigma = 1, periods = 252, n_paths = 1e4, price0 = 100):

        """
        Simulate log normal distribution of paths :

            - generate values with mu and sigma
            - cumsum each path
            - transform log returns to simple returns : use exp()

            * log returns : np.log(price1 / price0)
                - additive : use cumsum

                np.log(110 / 100)
                Out[0]: 0.09531017980432493

            * simple returns : np.exp(np.log(price1 / price0))
                - multiplicative : use cumprod

                np.exp(np.log(110 / 100))
                Out[1]: 1.1

        Returns: tuple,
                    - DataFrame with paths
                    - Terminal, pd.Series with Terminal Values for each Path

        """
        kwargs = {'mu' : mu, 'sigma' : sigma,
                 'periods' : periods, 'n_paths' : n_paths }
        df = randomWalk(**kwargs)
        paths = 100 * np.exp(df) # e^r transformation
        terminal = paths.loc[paths.index[-1]]
        return paths, terminal

    kwargs = {'mu' : 0.06, 'sigma' : 0.4,
              'periods' : 252, 'n_paths' : 1e4, 'price0' : 100 }

    paths, terminal = logNormalReturnPaths(**kwargs)

    print(f'Paths Terminal Values : mean {terminal.mean()}, std {terminal.std()}')

    # paths.loc[:, :5].plot(grid = True)
    # plt.show()
    # terminal.hist(bins = 30, grid = True)
    # plt.show()


# python3
"""Helper functions."""

import itertools
import math
import numpy as np

def to_rad(angle) -> float:
  """Convert angle (in degree) to radiants."""

  return math.pi / 180 * angle

def to_deg(rad) -> float:
  """Convert radiants to angle."""

  return rad / math.pi * 180.0

def bitprod(nbits):
  """Produce the iterable cartesian of nbits {0, 1}."""

  for bits in itertools.product([0, 1], repeat=nbits):
    yield bits

def bits2val(bits):
  """For a given enumeratable bits, compute the corresponding decimal integer."""

  # We assume bits are given in high to low order. For example,
  # the bits [1, 1, 0] will produce the value 6.
  return sum(v * (1 << (len(bits)-i-1)) for i, v in enumerate(bits))

def val2bits(val, nbits):
  """Convert decimal integer to list of {0, 1}."""

  # We return the bits in order high to low. For example,
  # the value 6 is being returned as [1, 1, 0].
  return [int(c) for c in format(val, '0{}b'.format(nbits))]

def bits2frac(bits, length):
  """For a given enumeratable bits, compute the binary fraction."""

  return sum(bits[i] * 2**(-i-1) for i in range(length))

def density_to_cartesian(rho):
  """Compute Bloch sphere coordinates from 2x2 density matrix."""

  a = rho[0, 0]
  b = rho[1, 0]
  x = 2.0 * b.real
  y = 2.0 * b.imag
  z = 2.0 * a - 1.0

  return np.real(x), np.real(y), np.real(z)

def qubit_to_bloch(psi):
  """Compute Bloch spere coordinates from 2x1 state vector/qubit."""

  return density_to_cartesian(psi.density())

def dump_bloch(x, y, z):
  """Textual output for Bloch sphere coordinates."""

  print('x: {:.2f}, y: {:.2f}, z: {:.2f}'.format(x, y, z))

def pi_fractions(val, pi='pi') -> str:
  """Convert a value in fractions of pi."""

  if val is None:
    return ''
  if val == 0:
    return '0'
  for pi_multiplier in range(1, 2):
    for frac in range(-128, 128):
      if frac and math.isclose(val, pi_multiplier * math.pi / frac):
        pi_str = ''
        if pi_multiplier != 1:
          pi_str = '{}*'.format(abs(pi_multiplier))
        if frac == -1:
          return '-{}{}'.format(pi_str, pi)
        if frac < 0:
          return '-{}{}/{}'.format(pi_str, pi, -frac)
        if frac == 1:
          return '{}{}'.format(pi_str, pi)
        return '{}{}/{}'.format(pi_str, pi, frac)

  # couldn't find fractional, just return original value.
  return f'{val}'


from functools import lru_cache
from itertools import product
from collections import namedtuple

from sympy import Symbol, Integer, poly, Eq, expand, zeros, symbols, Matrix, factorial, IndexedBase, solve, simplify

from commons import *

nature = namedtuple('nature', ['is_ordinary', 'is_exponential'])

# the following are indexed bases relative to commonly known RAs
mathcal_R, mathcal_P, mathcal_C, mathcal_S = (
    IndexedBase(r'\mathcal{R}'), # stands for the abstract RA
    IndexedBase(r'\mathcal{P}'), # stands for the Pascal triangle
    IndexedBase(r'\mathcal{C}'), # stands for the Catalan triangle
    IndexedBase(r'\mathcal{S}'), # stands for the Stirling triangle
)

gencoeff_d = IndexedBase('d') # stands for a generic coeff in a matrix

def riordan_matrix_exponential(RA):
    return lambda i,j: factorial(i)*RA(i,j)/factorial(j)

def riordan_matrix_by_convolution(dim, d, h):

    t = symbols('t')                                      # Local symbol to denote a formal variable `t`.

    with lift_to_Lambda(d, return_eq=True) as D:          # Lift equations `d` and `h` to become callables
      with lift_to_Lambda(h, return_eq=True) as H:        # objects, returning equations as well, in order
        d_eq, h_eq = D(t), H(t)                           # to let both of them depend on variable `t`.

    @lru_cache(maxsize=None)
    def column(j):                                        # Columns are memoized for the sake of efficiency.
        if not j: return d_eq                             # Base case.
        lhs = column(j-1).lhs * h_eq.lhs                  # Otherwise, use already computed column to build
        rhs = column(j-1).rhs * h_eq.rhs                  # the current `lhs` and `rhs`.
        return Eq(lhs, rhs)

    @lru_cache(maxsize=None)
    def C(j):                                             # Local function that performs Taylor expansion
        return column(j).rhs.series(t, n=dim).removeO()   # of columns, which are symbolic terms up to now.

    return lambda i, j: C(j).coeff(t, i).expand()         # Return a lambda to be plugged in a `Matrix` ctor.

def riordan_matrix_by_AZ_sequences(dim, seqs, init={(0,0):1},
                                   ctor=zeros, post=expand,
                                   lattice=None):

    if not lattice: lattice = [(n, k) for n in range(1, dim)
                                      for k in range(n+1)]

    Zseq, Aseq = seqs
    t = symbols('t')

    with lift_to_Lambda(Zseq) as Z, lift_to_Lambda(Aseq) as A:
        Z_series = Z(t).series(t, n=dim).removeO()
        A_series = A(t).series(t, n=dim).removeO()

    R = ctor(dim)

    for cell, i in init.items(): R[cell] = i

    for n, k in lattice:

        if k:
            v = sum(R[n-1, j] * A_series.coeff(t, n=i)
                    for i, j in enumerate(range(k-1, dim)))
        else:
            v = sum(R[n-1, j] * Z_series.coeff(t, n=j)
                    for j in range(dim))

        R[n, k] = v

    if callable(post): R = R.applyfunc(post)

    return lambda n, k: R[n, k]

def riordan_matrix_by_recurrence(dim, rec, init={(0,0):1},
                                 ctor=zeros, post=expand,
                                 lattice=None):

    if not lattice:
        lattice = [(n, k) for n in range(1, dim)   # `lattice` denotes the order in which coeffs in
                          for k in range(n+1)]     # the array will be computed; it can be plugged in.

    R = ctor(dim)                                  # Initial array as base for recursive construction.

    for cell, i in init.items(): R[cell] = i       # Set boundary values for the recurrence `rec`.

    for cell in lattice:                           # Visit cells as they appear in the order `lattice`;
        for comb_cell, v in rec(*cell).items():    # then, get dependencies with respect the each cell.

            try:
                comb = (1 if cell == comb_cell
                        else R[comb_cell])         # If it is possible to access the dependee cell,
                combined = v * comb                # then perform the combination using the given `v`.
            except IndexError:
                combined = 0                       # Otherwise, take `0` because combination fails.

            R[cell] += combined                    # Finally, accumulate the current combination.

    if callable(post): R = R.applyfunc(post)       # If some post processing is desired, do it.

    return lambda n, k: R[n, k]                    # Return a lambda that uses the computed `R`.

def diagonal_matrix(seq, default=0):

    def D(n, k):
        return seq[n] if n == k else default

    return D

def identity_matrix():

    def I(n, k):
        return 1 if n == k else 0

    return I

def frobenius_matrix(seq):

    m = len(seq)
    def F(n, k):
        return 1 if n+1 == k else -seq[-1-k] if n == m-1 else 0

    return F

def unit_vector(i, offset=-1):

    def U(n, k):
        return 1 if n == i+offset else 0

    return U

def Asequence(M):

    A = zeros(M.rows-1, M.cols)
    t = symbols('t')
    current_row_poly = M[0,0]

    for i in range(A.rows):

        next_row_poly = sum(M[i+1,j]*t**(i+1-j) for j in range(i+2))
        div_poly = (next_row_poly/current_row_poly).series(t, n=A.cols).removeO()

        for j in range(A.cols):
            A[i, j] = div_poly.coeff(t, n=j)

        current_row_poly = next_row_poly

    return A

def one(i):
    return 1

def columns_symmetry(M):
    return lambda i, j: M[i, i-j]

def rows_shift_matrix(by):
    return lambda i, j: 1 if i + by == j else 0

def diagonal_func_matrix(f):
    return lambda i, j: f(i) if i == j else 0

def production_matrix(M, exp=False):

    f = factorial if exp else one
    U = Matrix(M.rows, M.cols, rows_shift_matrix(by=1))
    F = Matrix(M.rows, M.cols, diagonal_func_matrix(f))
    F_inv = F**(-1)
    V = F_inv * U * F
    O = F_inv * M * F
    O_inv = O**(-1)
    PM = F * O_inv * V * O * F_inv
    PM = F_inv * PM * F if exp else PM
    PM = PM[:-1, :-1]
    return PM.applyfunc(simplify)

def inspect(M):

    P = production_matrix(M, exp=False)                    # Ordinary production matrix.
    C = production_matrix(M, exp=True)                     # "Exponential" production matrix.

    is_ord = all(P[:1-i, 1] == P[i-1:, i]                  # A RA is ordinary if columns of its PM
                 for i in range(2, P.cols))                # are shifted but equals.

    diagonals = {d: [C[j+d,j] for j in range(1,C.rows-d)]  # Fetch matrix diagonals for the
                 for d in range(C.rows-3) }                # exponential case.

    is_exp = all(map(is_arithmetic_progression,            # A Riordan array is exponential if `diagonals`
                     diagonals.values()))                  # are arithmetic progressions, all of them.

    return nature(is_ord, is_exp)

def is_arithmetic_progression(prog):

    steps = len(prog)-1
    for _ in range(steps):
        prog = [(b-a).simplify()                   # Reduce the list by consecutive differences,
                for a, b in zip(prog, prog[1:])]   # till a single object survives.

    assert len(prog) == 1                          # Consistency check of the last comment.

    return prog.pop() == 0                         # Finally, the reduced object has to vanish.

def compositional_inverse(h_eq, y=symbols('y'), check=True):

    spec, body = h_eq.lhs, h_eq.rhs                   # Destructuring function $h$ denoted by `h_eq`,
    t, = spec.args                                    # let `t` be the formal var of function $h$.
                                                      # $\bar{h}(h(t)) = t \leftrightarrow \left[ \bar{h}(y) = t\, | \, y = h(t) \right]$ allows us
    sols = solve(Eq(y, body), t)                      # to solve $y = h(t)$ with respect to $t$ because
    for sol in sols:                                  # $h(t)$ is known, denoted by `body`. For each
        L = Lambda(y, sol)                            # solution `sol`, which depends on $y$, we build
        if L(0) == 0:                                 # a callable object `L`. If it vanishes in $0$ and
                                                      # $\bar{h}(h(t))=t$, then it is a compositional
            if check: assert L(body).simplify() == t  # inverse of $h$.

            h_bar = Function(r'\bar{{ {} }}'.format(  # Prepare the name for function $\bar{h}$ and
                str(spec.func)))
            eq = Eq(h_bar(y), sol.factor())           # build the corresponding equation that defines
            return eq                                 # $\bar{h}(y)$, compositional inverse of $h(t)$.

    raise ValueError                                  # If the above code fails to return, raise an error.

def group_inverse(d_eq, h_eq, post=identity, check=True):

    t, y = symbols('t y')                                   # Formal symbols for symbolic functions $f$ and $g$.
    g_fn, f_fn = Function('g'), Function('f')

    with lift_to_Lambda(d_eq, return_eq=False) as D:        # Promote equations `d` and `h` to become
      with lift_to_Lambda(h_eq, return_eq=True) as H:       # callables objects, returning equations as well.
        f_eq = compositional_inverse(H(t), y, check)        # Let function $f$ be the compositional inverse of
        with lift_to_Lambda(f_eq, return_eq=False) as F:    # function $h$, then promote it as callable and
            F_t = F(t)                                      # (i) evaluate it at $t$;
            g = post(1/D(F_t))                              # (ii) build and refine $g$, the first function of
            f = post(F_t)                                   # the new Ra; (iii) refine $f$, the snd function.

    g_eq = Eq(g_fn(t), g.simplify())                        # Build corresponding equation objects with
    f_eq = Eq(f_fn(t), f.simplify())                        # simplified expressions.

    if check:                                               # If it is required to certify the computation,
        with lift_to_Lambda(g_eq, return_eq=False) as G:    # then promote the just built equations `g` and
          with lift_to_Lambda(f_eq, return_eq=False) as F:  # `f` to perform group operation in order to
            H_rhs = H(t).rhs                                # check that, it yields the identity element,
            assert (D(t)*G(H_rhs)).simplify() == 1          # which has (i) $1$ as first component and
            assert F(H_rhs).simplify() == t                 # (ii) $t$ as second component.

    return g_eq, f_eq


import numpy as np
from Forward_Kinematics import Forward_Kinematics

def 3Rrobot_FK(q1,q2,q3,unit='deg',plotting=False):

    # constants
    L1 = 4 # m
    L2 = 3 # m
    L3 = 2 # m

    # DH Parameters
    # for link 1    2    3    H
    a = [      0,  L1,   L2, L3]
    alpha = [  0,   0,    0,  0]
    d = [      0,   0,    0,  0]
    theta = [ q1,  q2,  q3,   0]

    if unit.lower() == 'rad':
        alpha = np.deg2rad(alpha)

    # Forward Kinematics
    T_0N = Forward_Kinematics(a, alpha, d, theta, unit=unit, plotting=plotting)
    return T_0N

if __name__ == "__main__":

    print(puma560_FK(np.deg2rad(90),0,np.deg2rad(90),0,0,0,unit='rad',plotting=True))

# 3DOF 3R Robot

# Joint Variables
#    theta1   theta2   theta3
# q = [  0,       0,        0]
# q = [ 10,       20,       30]
q = [ 90,       90,       90]

# D-H Parameters
Robot_3R = pd.DataFrame({
    #    link(i):  1   2   3   H
    'a(i-1)':     [0, L1, L2, L3],
    'alpha(i-1)': [0,  0,  0,  0],
    'd(i)':       [0,  0,  0,  0],
    'theta(i)':   [q[0], q[1], q[2], 0]
})

Robot_3R


import scipy as sp
import FiniteDiff as FD

def Jacobian(func, inputs, step_size=1e-7, fdtype='c'):
    """Calculate the Jacobian of a function.

    INPUTS:
        func:       Function handle to use for Jacobian
        inputs:     Input values to function
        step_size:  Step size
        fdtype:     finite difference type ('c'entered, 'f'orward, 'b'ackward)

    RETURNS:
        jacobian:   An mxn array as specified by the tuple dim.
    """

    #test our return
    #try to find dimensions by being smart
    try:
        ndim = len(func(*inputs)), len(inputs)
    except TypeError:
        ndim = 1, len(inputs)

    jacobian = sp.zeros(ndim)

    if fdtype is 'c':
        for j in range(ndim[1]):
            jacobian[:,j] = FD.cdiff(func, inputs, vary=[j], accur=6, degree=1, tol=step_size)
    elif fdtype in ['f', 'b']:
        for j in range(ndim[1]):
            jacobian[:,j] = FD.fbdiff(func, inputs, vary=[j], accur=3, degree=1, direction=fdtype, tol=step_size)

    return jacobian

def eval_hessian(func, inputs, varyi, varyj, tol):
    #inner_pval = [x+h*tol if index in vary else x for x,index in zip(xvals, range(len(xvals)))]

    inputs = sp.asarray(inputs)
    varyi = sp.asarray(varyi)
    varyj = sp.asarray(varyj)

    f1 = func(*(inputs+(varyi+varyj)*tol))
    f2 = func(*(inputs+(varyi-varyj)*tol))
    f3 = func(*(inputs+(varyj-varyi)*tol))
    f4 = func(*(inputs-(varyi+varyj)*tol))

    test = (f1-f2-f3+f4)/(4.0*tol**2)
    return test

def Hessian(func, inputs, tol=1e-5):
    ndim = [len(inputs)]*2

    hessian = sp.zeros(ndim)

    #cache our elementary vectors
    elists = []
    for j in range(ndim[0]):
        vj=[0]*(ndim[0]-1)
        vj.insert(j,1)
        elists.append(vj)

    for i in range(ndim[0]):
        for j in range(ndim[0]):
            hessian[i,j] = eval_hessian(func, inputs, elists[i], elists[j], tol)

    return hessian


import scipy.optimize as opt
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def set_axes_radius(ax, origin, radius):
    # https://stackoverflow.com/a/50664367
    ax.set_xlim3d([origin[0] - radius, origin[0] + radius])
    ax.set_ylim3d([origin[1] - radius, origin[1] + radius])
    ax.set_zlim3d([origin[2] - radius, origin[2] + radius])

def set_axes_equal(ax):
    '''Make axes of 3D plot have equal scale so that spheres appear as spheres,
    cubes as cubes, etc..  This is one possible solution to Matplotlib's
    ax.set_aspect('equal') and ax.axis('equal') not working for 3D.

    Input
      ax: a matplotlib axis, e.g., as output from plt.gca().
    '''

    limits = np.array([
        ax.get_xlim3d(),
        ax.get_ylim3d(),
        ax.get_zlim3d(),
    ])

    origin = np.mean(limits, axis=1)
    radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0]))
    set_axes_radius(ax, origin, radius)

def solve(xyz, tol=1.0e-10):
    """ Solve for normal vector and regression error using la.lstsq
        Expects xyz to be a numpy array with the data
        x, y, and z as column vectors
    """
    means = xyz.mean(0)
    Nml = xyz - means
    idx = np.argmin(Nml.max(0) - Nml.min(0))
    indices = [0, 1, 2]
    indices.remove(idx)
    M = Nml[:, indices]
    w = Nml[:, idx]

    result = opt.lsq_linear(M, w, tol=tol)
    x = result.x
    normal = np.insert(-x[:], idx, 1)
    return normal, means, result

def test(n=1000, noise=10, offset=50, tol=1.0e-10):
    """
        Test the least squares algorithm with a point cloud
        with a given number of points, noise level and offset
    """
    p = (np.random.rand(3) - 0.5)
    p[0] = 0
    xyz = (np.random.rand(n, 3) - 0.5)*100
    xyz[:, 0] = xyz@p + (np.random.rand(n)-.5)*noise + offset
    normal, means, result = solve(xyz, tol=tol)
    print("normal vector 'normal': ", normal)
    print("center point 'means': ", means)
    print("least square solution 'p': ", result.x)
    print("least square solution 'cost': ", result.cost)
    print("LS residual squared 'fun': ", result.fun)
    print("Active_mask 'active_mask': ", result.active_mask)
    print("nit 'nit': ", result.nit)
    print("status 'status': ", result.status)
    print("msg 'message': ", result.message)
    print("success 'success': ", result.success)

    fig = plt.figure()
    ax = fig.gca(projection='3d')
    ax.set_aspect('equal')
    ax.scatter(xyz[:, 0], xyz[:, 1], xyz[:, 2])
    data = list(zip(means, means + normal * 10))
    ax.plot(*data, 'r')
    set_axes_equal(ax)
    plt.show()


import numpy as np
from matplotlib import pyplot as plt

np.set_printoptions(precision=6)

def plotData(X, y):
    """Plots the data points X and y into a new figure

    plotData(x,y) plots the data points with + for the positive examples
    and o for the negative examples. X is assumed to be a Mx2 matrix.
    :param X: the exam scores
    :param y: the label
    :return:
    """
    positive = y == 1
    negative = y == 0
    plt.figure()
    plt.plot(X[positive, 0], X[positive, 1], 'k+', markeredgewidth=2, markersize=7)
    plt.plot(X[negative, 0], X[negative, 1], 'ko', markerfacecolor='y', markersize=7)

def sigmoid(z):
    """Compute sigmoid function

    g = sigmoid(z) computes the sigmoid of z.
    :param z:
    :return:
    """
    g = np.frompyfunc(lambda x: 1 / (1 + np.exp(-x)), 1, 1)
    return g(z).astype(z.dtype)

def costFunction(theta, X, y):
    """Compute cost and gradient for logistic regression

    J = costFunction(theta, X, y) computes the cost of using theta as the
    parameter for logistic regression and the gradient of the cost
    w.r.t. to the parameters.
    :param theta:
    :param X:
    :param y:
    :return:
    """
    # Initialize some useful values
    m = len(y)  # number of training examples

    h_theta = sigmoid(np.matmul(X, theta))
    ones = np.ones(m)
    J = -1 / m * (np.matmul(y, np.log(h_theta)) + np.matmul(ones - y, np.log(ones - h_theta)))
    grad = 1 / m * np.matmul(X.transpose(), h_theta - y)
    return J, grad

def mapFeature(X1, X2):
    """Feature mapping function to polynomial features

    mapFeature(X1, X2) maps the two input features
    to quadratic features used in the regularization exercise.
    :param X1:
    :param X2:
    :return: a new feature array with more features, comprising of
    X1, X2, X1.^2, X2.^2, X1*X2, X1*X2.^2, etc..
    """
    degree = 6
    out = np.ones(X1.shape[0])
    for i in range(1, degree + 1):
        for j in range(i + 1):
            out = np.column_stack([out, X1 ** (i - j) * X2 ** j])
    return out

def plotDecisionBoundary(theta, X, y):
    """Plots the data points X and y into a new figure with
    the decision boundary defined by theta

    plotDecisionBoundary(theta, X,y) plots the data points with + for the
    positive examples and o for the negative examples.
    :param theta:
    :param X: either
    1) Mx3 matrix, where the first column is an all-ones column for the
       intercept.
    2) MxN, N>3 matrix, where the first column is all-ones
    :param y:
    :return:
    """
    # Plot Data
    plotData(X[:, 1:], y)
    if X.shape[1] <= 3:
        # Only need 2 points to define a line, so choose two endpoints
        plot_x = np.array([min(X[:, 1]) - 2, max(X[:, 1]) + 2])

        # Calculate the decision boundary line
        plot_y = -1 / theta[2] * (theta[1] * plot_x + theta[0])

        # Plot, and adjust axes for better viewing
        plt.plot(plot_x, plot_y)

        # Legend, specific for the exercise
        plt.legend(['Admitted', 'Not admitted', 'Decision Boundary'], loc='upper right')
        plt.xlim([30, 100])
        plt.ylim([30, 100])
    else:
        # Here is the grid range
        u = np.linspace(-1, 1.5, 50)
        v = np.linspace(-1, 1.5, 50)

        z = np.zeros((len(u), len(v)))
        # Evaluate z = theta * x over the grid
        for i in range(len(u)):
            for j in range(len(v)):
                z[i, j] = np.matmul(mapFeature(u[i:i + 1], v[j:j + 1]), theta)

        u, v = np.meshgrid(u, v)
        #
        # Plot z = 0
        # Notice you need to specify the range[0, 0]
        cs = plt.contour(u, v, z.transpose(), [0], linewidth=2, colors='green')
        cs.collections[0].set_label('')

def predict(theta, X):
    """Predict whether the label is 0 or 1 using learned logistic
    regression parameters theta

    p = predict(theta, X) computes the predictions for X using a
    threshold at 0.5 (i.e., if sigmoid(theta'*x) >= 0.5, predict 1)
    :param theta:
    :param X:
    :return:
    """
    p = sigmoid(np.matmul(X, theta)) >= 0.5
    return p.astype(np.int)

def costFunctionReg(theta, X, y, lambda_):
    """Compute cost and gradient for logistic regression with regularization

    J = costFunctionReg(theta, X, y, lambda) computes the cost of using
    theta as the parameter for regularized logistic regression and the
    gradient of the cost w.r.t. to the parameters.
    :param theta:
    :param X:
    :param y:
    :param lambda_:
    :return:
    """
    # Initialize some useful values
    m = len(y)  # number of training examples
    J, grad = costFunction(theta, X, y)
    J += lambda_ / (2 * m) * np.matmul(theta[1:], theta[1:])
    grad[1:] += lambda_ / m * theta[1:]
    return J, grad


# Online Bayesian linear regression in 1d using Kalman Filter
# Based on: https://github.com/probml/pmtk3/blob/master/demos/linregOnlineDemoKalman.m

# The latent state corresponds to the current estimate of the regression weights w.
# The observation model has the form
# p(y(t) |  w(t), x(t)) = Gauss( C(t) * w(t), R(t))
# where C(t) = X(t,:) is the observation matrix for step t.
# The dynamics model has the form
# p(w(t) | w(t-1)) = Gauss(A * w(t-1), Q)
# where Q>0 allows for parameter  drift.
# We show that the result is equivalent to batch (offline) Bayesian inference.

import jax.numpy as jnp

import matplotlib.pyplot as plt
from numpy.linalg import inv

from jsl.lds.kalman_filter import LDS, kalman_filter

def kf_linreg(X, y, R, mu0, Sigma0, F, Q):
    """
    Online estimation of a linear regression
    using Kalman Filters
    Parameters
    ----------
    X: array(n_obs, dimension)
        Matrix of features
    y: array(n_obs,)
        Array of observations
    Q: float
        Known variance
    mu0: array(dimension)
        Prior mean
    Sigma0: array(dimesion, dimension)
        Prior covariance matrix
    Returns
    -------
    * array(n_obs, dimension)
        Online estimation of parameters
    * array(n_obs, dimension, dimension)
        Online estimation of uncertainty
    """
    C = lambda t: X[t][None, ...]
    lds = LDS(F, C, Q, R, mu0, Sigma0)

    mu_hist, Sigma_hist, _, _ = kalman_filter(lds, y)
    return mu_hist, Sigma_hist

def posterior_lreg(X, y, R, mu0, Sigma0):
    """
    Compute mean and covariance matrix of a
    Bayesian Linear regression
    Parameters
    ----------
    X: array(n_obs, dimension)
        Matrix of features
    y: array(n_obs,)
        Array of observations
    R: float
        Known variance
    mu0: array(dimension)
        Prior mean
    Sigma0: array(dimesion, dimension)
        Prior covariance matrix
    Returns
    -------
    * array(dimension)
        Posterior mean
    * array(n_obs, dimension, dimension)
        Posterior covariance matrix
    """
    Sn_bayes_inv = inv(Sigma0) + X.T @ X / R
    Sn_bayes = inv(Sn_bayes_inv)
    mn_bayes = Sn_bayes @ (inv(Sigma0) @ mu0 + X.T @ y / R)

    return mn_bayes, Sn_bayes

def main():
    n_obs = 21
    timesteps = jnp.arange(n_obs)
    x = jnp.linspace(0, 20, n_obs)
    X = jnp.c_[jnp.ones(n_obs), x]
    F = jnp.eye(2)
    mu0 = jnp.zeros(2)
    Sigma0 = jnp.eye(2) * 10.

    Q, R = 0, 1
    # Data from original matlab example
    y = jnp.array([2.4865, -0.3033, -4.0531, -4.3359, -6.1742, -5.604, -3.5069, -2.3257, -4.6377,
                   -0.2327, -1.9858, 1.0284, -2.264, -0.4508, 1.1672, 6.6524, 4.1452, 5.2677, 6.3403, 9.6264, 14.7842])

    # Online estimation
    mu_hist, Sigma_hist = kf_linreg(X, y, R, mu0, Sigma0, F, Q)
    kf_var = Sigma_hist[-1, [0, 1], [0, 1]]
    w0_hist, w1_hist = mu_hist.T
    w0_err, w1_err = jnp.sqrt(Sigma_hist[:, [0, 1], [0, 1]].T)

    # Offline estimation
    (w0_post, w1_post), Sigma_post = posterior_lreg(X, y, R, mu0, Sigma0)
    w0_std, w1_std = jnp.sqrt(Sigma_post[[0, 1], [0, 1]])

    dict_figures = {}

    fig, ax = plt.subplots()
    ax.errorbar(timesteps, w0_hist, w0_err, fmt="-o", label="$w_0$", color="black", fillstyle="none")
    ax.errorbar(timesteps, w1_hist, w1_err, fmt="-o", label="$w_1$", color="tab:red")

    ax.axhline(y=w0_post, c="black", label="$w_0$ batch")
    ax.axhline(y=w1_post, c="tab:red", linestyle="--", label="$w_1$ batch")

    ax.fill_between(timesteps, w0_post - w0_std, w0_post + w0_std, color="black", alpha=0.4)
    ax.fill_between(timesteps, w1_post - w1_std, w1_post + w1_std, color="tab:red", alpha=0.4)

    plt.legend()
    ax.set_xlabel("time")
    ax.set_ylabel("weights")
    ax.set_ylim(-8, 4)
    ax.set_xlim(-0.5, n_obs)
    dict_figures["linreg_online_kalman"] = fig

    return dict_figures

if __name__ == "__main__":
    from jsl.demos.plot_utils import savefig
    plt.rcParams["axes.spines.right"] = False
    plt.rcParams["axes.spines.top"] = False
    dict_figures = main()
    savefig(dict_figures)
    plt.show()


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

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

def generate_offset_normal_data(D,M,K=None,count=10,sigma=0.9):
    '''Generate M class dataset with X normally distributed with eye covariance matrix with sigma^2 on diagonal
    maen(X)=0 everywhere except 1 for particular features subset fixed for each class.'''

    if K is None:
        K=int(D/M)

    X=np.zeros( (0,D) )
    Y=[]
    cov = sigma*np.eye(D)

    for m in range(M):
        mean=np.zeros(D)
        #mean=-np.ones(D)*K/(D-K)
        mean[m*K:m*K+K] = 1
        #mean[:] = m*np.random.uniform(0,1,D)
        X = np.vstack( (X, np.random.multivariate_normal(mean,cov,count) ) )
        Y+=count*[m]

    Y=np.array(Y)
    return (X,Y)


# global
import numpy as np
import ivy as _ivy
from typing import Union, Optional, Tuple, Literal
from collections import namedtuple

# local
from ivy import inf
import ivy as _ivy
from collections import namedtuple

# noinspection PyUnusedLocal,PyShadowingBuiltins
def vector_norm(x: np.ndarray,
                axis: Optional[Union[int, Tuple[int]]] = None,
                keepdims: bool = False,
                ord: Union[int, float, Literal[inf, - inf]] = 2)\
                 -> np.ndarray:

    if axis is None:
        np_normalized_vector = np.linalg.norm(x.flatten(), ord, axis, keepdims)

    else:
        np_normalized_vector = np.linalg.norm(x, ord, axis, keepdims)

    if np_normalized_vector.shape == tuple():
        return np.expand_dims(np_normalized_vector, 0)
    return np_normalized_vector

def svd(x:np.ndarray,full_matrices: bool = True) -> Union[np.ndarray, Tuple[np.ndarray,...]]:
    results=namedtuple("svd", "U S Vh")
    U, D, VT=np.linalg.svd(x, full_matrices=full_matrices)
    res=results(U, D, VT)
    return res

def diagonal(x: np.ndarray,
             offset: int = 0,
             axis1: int = -2,
             axis2: int = -1) -> np.ndarray:
    return np.diagonal(x, offset=offset, axis1=axis1, axis2=axis2)

def slogdet(x:Union[_ivy.Array,_ivy.NativeArray],full_matrices: bool = True) -> Union[_ivy.Array, Tuple[_ivy.Array,...]]:
    results = namedtuple("slogdet", "sign logabsdet")
    sign, logabsdet = np.linalg.slogdet(x)
    res = results(sign, logabsdet)
    return res


import networkx as nx
from sys import argv

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

def cycle_betweenness (G): # taking a graph and returning the cycle betweenness

	cn = {v:0 for v in G.nodes()} # zeroing the cycle count for nodes
	cp = {} # the cycle count for node pairs

	for b in nx.simple_cycles(G): # going over all simple cycles of G

		for d in b: # incrementing the count for nodes
			cn[d] += 1

		for i in range(len(b)): # incrementing the count for links
			u = b[i]
			if i < len(b) - 1:
				v = b[i + 1]
			else:
				v = b[0]

			if u > v: # sorting the node pairs, because in the output, we don't care about direction
				w = u
				u = v
				v = w

			e = (u,v) # assigning the node pair

			if e not in cp.keys(): # incrementing the counter
				cp[e] = 1
			else:
				cp[e] += 1

	return cn, cp

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

if __name__ == '__main__':

	if len(argv) != 2:
		print('usage: python3 cycle_betweenness.py [input file]')
		exit(1)

	G = nx.read_edgelist(argv[1], create_using = nx.DiGraph)

	cn, cp = cycle_betweenness(G)

	print('=== NODES ===')
	for v,betw in cn.items():
		print(v,betw)

	print('\n=== CONNECTED NODE PAIRS ===')
	for p,betw in cp.items():
		print('(' + p[0] + ',' + p[1] + ')', betw)

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


# exec(open(".//04_Numpy_Axis.py"))
print "Executing Numpy Axis"

import numpy as np

# Change False to True for this block of code to see what it does

# NumPy axis argument
if True:
    a = np.array([
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]
    ])

    print a.sum()
    print a.sum(axis=0)
    print a.sum(axis=1)

# Subway ridership for 5 stations on 10 different days
ridership = np.array([
    [   0,    0,    2,    5,    0],
    [1478, 3877, 3674, 2328, 2539],
    [1613, 4088, 3991, 6461, 2691],
    [1560, 3392, 3826, 4787, 2613],
    [1608, 4802, 3932, 4477, 2705],
    [1576, 3933, 3909, 4979, 2685],
    [  95,  229,  255,  496,  201],
    [   2,    0,    1,   27,    0],
    [1438, 3785, 3589, 4174, 2215],
    [1342, 4043, 4009, 4665, 3033]
])

def min_and_max_riders_per_day(ridership):
    '''
    Fill in this function. First, for each subway station, calculate the
    mean ridership per day. Then, out of all the subway stations, return the
    maximum and minimum of these values. That is, find the maximum
    mean-ridership-per-day and the minimum mean-ridership-per-day for any
    subway station.
    '''

    # Find the mean ridership per day for each subway station
    station_riders = ridership.mean(axis=0)

    # Find the maximum daily ridership and the minimum
    max_daily_ridership = station_riders.max()
    min_daily_ridership = station_riders.min()

    return (max_daily_ridership, min_daily_ridership)

import pandas as pd
weekData = pd.DataFrame({
    'temp': [70, 71, 72, 73, 74, 75, 76 ],
    'condition': ['Cloudy','Clear','Windy','Clear','Stormy','Cloudy','Clear'],
    'it_rained':[False, False, False, False, True, True, False]}, index = [
            'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'
        ])


#!/Users/williamrobertmurphy/anaconda3/envs/env36/bin/python3.6
import os
import sys
import abc
import re
import math
import collections
import numpy as np
import pandas as pd

def bubble_sort(l):
    """
    bubble sort algorithm:
    Time Complexity: O(n^2)
    """
    j = 0
    for i in range(len(l)-1):
        j = i+1
        while j < len(l):
            if l[i] > l[j]:
                temp = l[j]
                l[j] = l[i]
                l[i] = temp
            j += 1
    return l

def merge(L: list, R: list, A: list):
    nL = len(L)
    nR = len(R)
    i = 0
    j = 0
    k = 0
    while i < nL and j < nR:
        if L[i] <= R[j]:
            A[k] = L[i]
            i += 1
        else:
            A[k] = R[j]
            j += 1
        k += 1
    while i < nL:
        A[k] = L[i]
        i += 1
        k += 1
    while j < nR:
        A[k] = R[j]
        j += 1
        k += 1
    return A

def merge_sort(l):
    """
    merge sort algorithm
    Time Complexity: O(nlogn)
    """
    n = len(l)
    if n < 2:
        return
    mid = math.floor(n/2)
    left = [i for i in l[:mid]]
    right = [i for i in l[mid:]]
    merge_sort(left)
    merge_sort(right)
    merge(left, right, l)
    return merge(left, right, l)


"""
A company wants to determine the linear relationship between the selling price of their product in US dollars
 and the number of units sold in thousands.
Perform a linear regression on the following data to determine the linear predictor function
"""

from scipy.stats import linregress

x = [12, 14, 16, 18, 20]
y = [54, 57, 49, 48, 42]

slope = round(linregress(x, y).slope, 1)
intercept = round(linregress(x, y).intercept, 1)

#formula
print(f" y = {intercept} + {slope}x")


""" Compute the cost and gradeint of a given set of labeled data
"""
import numpy as np
from typing import List

from sigmoid import sigmoid

def cost_function(theta, X, y):
    """Calculates cost of current theta paremeters
    Args:
        X: input features
        y: labels
        theta: weights for input features

    Returns:
        Cost and Gradient
    """
    grad = np.zeros(theta.shape)
    num_examples = y.size

    # X => (100, 3)
    # theta => (3, 1)
    # (X * theta) => (100 x 3) * (3 x 1) => (100 x 1)
    hypothesis = sigmoid(np.dot(X, theta))
    # y => (100 x 1)
    # y.T => (1 x 100)
    # y.T * log(X * theta) => (1 x 100) x (100 x 1) => (1 x 1) scalar
    '''
    Note: It appears the np.dot will handle converting a matrix to its
    transpose if it detects that the transpose will make the operation
    succeed. Hence, we don't need the tranpose of y, but we take it
    because it's right
    '''
    J = (1/num_examples) * np.sum(
                        np.dot(-y, np.log(hypothesis)) -
                        np.dot(1-y, np.log(1 - hypothesis))
                    )
    grad = (1/num_examples) * np.dot((hypothesis - y), X)

    return J, grad


import numpy as np
import scipy.stats as stats
from UQpy.Distributions.baseclass import DistributionND

class MVNormal(DistributionND):
    """
    Multivariate normal distribution having probability density function

    .. math:: f(x) = \dfrac{1}{\sqrt{(2\pi)^k\det\Sigma}}\exp{-\dfrac{1}{2}(x-\mu)^T\Sigma^{-1}(x-\mu)}

    where :math:`\mu` is the mean vector, :math:`\Sigma` is the covariance matrix, and :math:`k` is the dimension of
    `x`.

    **Inputs:**

    * **mean** (`ndarray`):
        mean vector, `ndarray` of shape `(dimension, )`
    * **cov** (`float` or `ndarray`):
        covariance, `float` or `ndarray` of shape `(dimension, )` or `(dimension, dimension)`. Default is 1.

    The following methods are available for ``MVNormal``:

    * ``cdf``, ``pdf``, ``log_pdf``, ``rvs``, ``fit``, ``moments``.
    """
    def __init__(self, mean, cov=1.):
        if mean is not None and cov is not None:
            if len(np.array(mean).shape) != 1:
                raise ValueError('Input mean must be a 1D array.')
            if isinstance(cov, (int, float)):
                pass
            else:
                if not (len(np.array(cov).shape) in [1, 2] and all(sh == len(mean) for sh in np.array(cov).shape)):
                    raise ValueError('Input cov must be a float or ndarray of appropriate dimensions.')
        super().__init__(mean=mean, cov=cov, order_params=['mean', 'cov'])

    def cdf(self, x):
        cdf_val = stats.multivariate_normal.cdf(x=x, **self.params)
        return np.atleast_1d(cdf_val)

    def pdf(self, x):
        pdf_val = stats.multivariate_normal.pdf(x=x, **self.params)
        return np.atleast_1d(pdf_val)

    def log_pdf(self, x):
        logpdf_val = stats.multivariate_normal.logpdf(x=x, **self.params)
        return np.atleast_1d(logpdf_val)

    def rvs(self, nsamples=1, random_state=None):
        if not (isinstance(nsamples, int) and nsamples >= 1):
            raise ValueError('Input nsamples must be an integer > 0.')
        return stats.multivariate_normal.rvs(
            size=nsamples, random_state=random_state, **self.params).reshape((nsamples, -1))

    def fit(self, data):
        data = self._check_x_dimension(data)
        mle_mu, mle_cov = self.params['mean'], self.params['cov']
        if mle_mu is None:
            mle_mu = np.mean(data, axis=0)
        if mle_cov is None:
            mle_cov = np.cov(data, rowvar=False)
        return {'mean': mle_mu, 'cov': mle_cov}

    def moments(self, moments2return='mv'):
        if moments2return == 'm':
            return self.get_params()['mean']
        elif moments2return == 'v':
            return self.get_params()['cov']
        elif moments2return == 'mv':
            return self.get_params()['mean'], self.get_params()['cov']
        else:
            raise ValueError('UQpy: moments2return must be "m", "v" or "mv".')


#!/usr/bin/env python

import numpy as np

def torsion(p1, p2, p3, p4, flg_degree=False, flg_360=False):
    '''
    p1, p2, p3, p4 are numpy arrays for four coordinates by which the torsion angle phi is defined.
    '''

    v12 = p1 - p2
    v32 = p3 - p2
    v34 = p3 - p4

    m = np.cross(v12, v32)
    n = np.cross(v32, v34)

    if np.dot(v12,n) >= 0.0: # 0 < phi < 180
        #phi =   math.acos( np.dot(m,n) / math.sqrt(np.dot(m,m)*np.dot(n,n)) )
        phi =   np.arccos( np.dot(m,n) / (np.linalg.norm(m) * np.linalg.norm(n)))

    else: # -180 < phi < 0
        #phi = - math.acos( np.dot(m,n) / math.sqrt(np.dot(m,m)*np.dot(n,n)) )
        phi = - np.arccos( np.dot(m,n) / (np.linalg.norm(m) * np.linalg.norm(n)))

        if flg_360: # convert to (180 < phi < 360)
            #phi = 2 * math.pi + phi
            phi = 2 * np.pi + phi

    if flg_degree:
        #return phi * 180.0 / math.pi
        return phi * 180.0 / np.pi
    else:
        return phi

def torsion_xy(p1, p2, p3, p4):
    '''
    p1, p2, p3, p4 are numpy arrays for four coordinates by which the torsion angle phi is defined.
    '''

    v12 = p1 - p2
    v32 = p3 - p2
    v34 = p3 - p4

    m = np.cross(v12, v32)
    n = np.cross(v32, v34)

    if np.dot(v12,n) >= 0.0: # 0 < phi < 180
        phi =   math.acos( np.dot(m,n) / math.sqrt(np.dot(m,m)*np.dot(n,n)) )

    else: # -180 < phi < 0
        phi = - math.acos( np.dot(m,n) / math.sqrt(np.dot(m,m)*np.dot(n,n)) )

    return math.cos(phi), math.sin(phi)

if __name__ == "__main__":
    print('Test of torsion.py')
    print ('''
    ATOM  22440  C4'   G A   0      24.717  92.775  96.702  1.00 31.19           C
    ATOM  22458  P     A A   1      22.332  94.740  95.607  1.00 34.55           P
    ATOM  22463  C4'   A A   1      20.808  97.714  93.719  1.00 34.31           C
    ATOM  22480  P     G A   2      18.954  96.788  90.471  1.00 34.67           P
    ATOM  22485  C4'   G A   2      19.175  99.605  87.735  1.00 33.40           C
    ''')

    a = np.array([ 24.717 , 92.775 , 96.702])
    b = np.array([ 22.332 , 94.740 , 95.607])
    c = np.array([ 20.808 , 97.714 , 93.719])
    d = np.array([ 18.954 , 96.788 , 90.471])
    e = np.array([ 19.175 , 99.605 , 87.735])

    print("Torsion of C4'-P-C4'-P")
    print('  flg_360=False(default): %f rad, %f deg' % (torsion(a,b,c,d), torsion(a,b,c,d,True)))
    print('  flg_360=True          : %f rad, %f deg' % (torsion(a,b,c,d,flg_360=True), torsion(a,b,c,d,True,flg_360=True)))
    print('  cos(phi), sin(phi)    : x = %f, y = %f' % torsion_xy(a,b,c,d))

    print("Torsion of P-C4'-P-C4'")
    print('  flg_360=False(default): %f rad, %f deg' % (torsion(b,c,d,e), torsion(b,c,d,e,True)))
    print('  flg_360=True          : %f rad, %f deg' % (torsion(b,c,d,e,flg_360=True), torsion(b,c,d,e,True,flg_360=True)))
    print('  cos(phi), sin(phi)    : x = %f, y = %f' % torsion_xy(b,c,d,e))


import numpy as np
import math

class TestCode:

    def __init__(self):

        pass

    def test_the_linear_acceleration(student_answer, measured_acceleration, phi, theta):
        psi = 0.0
        g = 9.81 * np.array([[0], [0], [-1]])
        r_x = np.array([[1, 0, 0],
                        [0, np.cos(phi), -np.sin(phi)],
                        [0, np.sin(phi), np.cos(phi)]])

        r_y = np.array([[np.cos(theta), 0, np.sin(theta)],
                        [0, 1, 0],
                        [-np.sin(theta), 0, np.cos(theta)]])

        r_z = np.array([[np.cos(psi), -np.sin(psi), 0],
                        [np.sin(psi), np.cos(psi), 0],
                        [0,0,1]])

        r = np.matmul(r_z, np.matmul(r_y,r_x))

        # calculating the true acceleration in body frame by removing the gravity component
        a_body_frame = measured_acceleration - np.matmul(r, g)

        # converting the true acceleration back to the inertial frame
        a_inertial_frame = np.matmul(np.linalg.inv(r), a_body_frame)

        epsilon = 10**(-4)
        if np.all(abs(student_answer- a_inertial_frame) < np.array([[1],[1],[1]])* epsilon):
            print('Test passed')
        else:
            print('Test failed')

    def test_the_accelerometer_measurement(student_answer, acctual_a, phi, theta, sigma_a=0.0):

        psi = 0.0
        g = 9.81 * np.array([[0], [0], [-1]])
        r_x = np.array([[1, 0, 0],
                        [0, np.cos(phi), -np.sin(phi)],
                        [0, np.sin(phi), np.cos(phi)]])

        r_y = np.array([[np.cos(theta), 0, np.sin(theta)],
                        [0, 1, 0],
                        [-np.sin(theta), 0, np.cos(theta)]])

        r_z = np.array([[np.cos(psi), -np.sin(psi), 0],
                        [np.sin(psi), np.cos(psi), 0],
                        [0,0,1]])

        r = np.matmul(r_z,np.matmul(r_y,r_x))

        # Converting global frame acceleration into the body frame acceleration
        acctual_a = acctual_a.reshape(3,1)
        linear_acc_bodyframe = np.matmul(r, acctual_a)

        # Adding the gravity component
        gravity_component = np.matmul(r, g)

        # Error component
        error_component = np.random.normal(0.0, sigma_a, (3, 1))

        measured_acceleration = linear_acc_bodyframe + gravity_component + error_component

        epsilon = 10**(-3)

        if np.all(abs(student_answer- measured_acceleration) < np.array([[1],[1],[1]])* epsilon):
            print('Test passed')
        else:
            print('Test failed')

    def test_the_gyroscope_measurement(student_answer,phi, theta, phi_dot, theta_dot, sigma_omega=0.0):
        # Conversion matrix
        R =np.array([[1, np.sin(phi)*np.tan(theta)], [0, np.cos(phi)]])

        # Body rate p and q true values
        body_rate = np.matmul(np.linalg.inv(R) , np.array([[phi_dot], [theta_dot]]))

        # Adding an error to the true body rates
        measured_bodyrates = body_rate + np.random.normal(0.0, sigma_omega, (2, 1))

        epsilon = 10**(-4)

        if np.all(abs(student_answer- measured_bodyrates) < np.array([[1],[1]])* epsilon):
            print('Test passed')
        else:
            print('Test failed')

Course04/17-12_Dead Reckoning In 3D/test_three_d_object.py


import numpy as np

def normalize(v):
    """
    Normalizes 1D (possibly complex) vector
    :param v: vector to normalize
    :return: normalized vector
    """
    return v / np.sqrt(v.dot(v.conjugate()))

def gram_schmidt(B):
    """
    Gram Schmidt algorithm used to orthonormalize matrix B.
    :param B: complex matrix. e.g. \n
              B = [[b_{0,0}],[b_{0,1}],
                  [[b_{1,0}],[b_{1,1}]]\n where b_{i,j} corresponds
              to the i-th element of column basis vector j
              and the goal is to orthonormalize the basis spanned by
              the columns of B
    :return: Orthonormalized matrix with same dimensions as B
    """
    B[:, 0] = normalize(B[:, 0])
    for i in range(1, B.shape[1]):
        Ai = B[:, i]
        for j in range(0, i):
            Aj = B[:, j]
            t = Ai.dot(Aj.conjugate())
            Ai = Ai - t * Aj
        B[:, i] = normalize(Ai)
    return B

def factors(n):
    """
    Returns all factors of n
    """
    import functools
    return list(functools.reduce(list.__add__,
                                 ([i, n // i] for i in range(1, int(n ** 0.5) + 1)
                                  if n % i == 0)))


from numpy import cos, cosh, sin, sinh
import numpy as np
import matplotlib.pyplot as plt
import time
'''
TASK 5

'''
r1 = 1.87527632324985
#r1 derived in previous task
'''
First Integral, steps 1-3
'''
#phi and psi functions as defined by the project statement
def phi(x, r):
    return (sin(r*x) + sinh(r*x) + ((cos(r)+cosh(r))/(sin(r)+sinh(r)))*(cos(r*x)-cosh(r*x)))

def d2phi(x, r):
    return (r**2)*(-sin(r*x) + sinh(r*x) + ((cos(r)+cosh(r))/(sin(r)+sinh(r)))*(-cos(r*x)-cosh(r*x)))

def psi_h(x, qi):
    return qi*phi(x, r1)

def d2psi_h(x, qi):
    return qi*d2phi(x, r1)

#single integral approximator
def trapezoid(r, qx, a=0, b=1, m=100):
    dx = (b-a)/m

    total = phi(a, r)*(d2psi_h(a, qx))
    total += phi(b, r)*(d2psi_h(b, qx))

    for i in range(1, m-1):
        total += 2*phi(a+i*dx, r)*(d2psi_h(a+i*dx, qx))

    return total*dx/2

#slope of the single integral approximator
def linear_slope(n=100, a=0, b=1):
    def h(x):
        return phi(x, r1)*d2phi(x, r1)

    const = (b-a)/(2*n)

    dx = (b-a)/(n)

    total = h(a) + h(b)

    for i in range(1, n):
        total += 2*h(a + dx*i)

    return const*total

#double integral approximator
def double_int(r, qx, a=0, b=1, ups=1, m=25):
    dx = (b-a)/m
    xs = np.arange(a, b, dx)

    total_x = []
    for x in xs:
        lws = x
        ds = (ups - lws)/m

        total_s = phi(x, r)*cos(psi_h(x, qx) -psi_h(lws, qx))
        total_s += phi(x, r)*cos(psi_h(x, qx) -psi_h(ups, qx))

        for i in range(1, m):
            total_s += 2*phi(x, r)*cos(psi_h(x, qx) -psi_h(lws + i*ds, qx))

        total_x.append(total_s*ds/2)

    ret = 2*sum(total_x) - total_x[0] - total_x[-1]
    return ret*dx/2

#function in equation 2 per project handout
def f(r, qx):
    return 100*double_int(r, qx) + trapezoid(r, qx)

#g function (for fixed point)
def g(r, qx):
    return -(100*double_int(r, qx) + trapezoid(r, qx))/(linear_slope())

# qi = 4.27775
# f1 = f(r1, qi)

n=100
lim = 5
q1 = np.arange(-lim, lim, 2*lim/n)
f1 = []                                 #matrix to hold the value of f1(q1) for all discrete points q1, q2
g1 = []
task7 = []

#evaluate all the points
for qi in q1:
    f1.append(f(r1, qi))
    if qi>=0:
        g1.append(g(r1, qi))

g1.append(g(r1, q1[-1]+2*lim/n))

f1_p = np.array(f1)

#plot function f(q1) in 1d
plt.plot(q1, f1_p)
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.title("Plot of f(q_1)")
plt.show()

'''
TASK 6
'''
#fixed-point stuff
qs = np.arange(0,5,10/n)
plt.plot(qs, g1)
plt.plot(qs, qs, 'r')
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.axvline(x=2.5, ymin=0.3, ymax=0.42, color='g', linestyle=':', linewidth=2)
plt.axvline(x=4, ymin=0.3, ymax=0.42, color='g', linestyle=':', linewidth=2)
plt.axhline(y=2.5, xmin=0.51, xmax=0.79, color='g', linestyle=':', linewidth=2)
plt.axhline(y=4, xmin=0.51, xmax=0.79, color='g', linestyle=':', linewidth=2)
plt.title("Fixed-point quadrature scheme")
plt.show()

'''
TASK 7
'''
#custom secant function
def secant(ra, rb, e):
    r = rb - f(r1, rb)*((rb-ra))/(f(r1,rb) - f(r1,ra))

    steps = 1

    while abs(r-rb) > e:
        ra = rb
        rb = r
        r = rb - f(r1, rb)*((rb-ra))/(f(r1,rb) - f(r1,ra))
        steps +=1

    #check how many steps it took
    print("found a solution with error {} in {} steps".format(str(e), str(steps)))
    return [r, steps]

root = secant(4, 4.1, 0.001)

print(root)

'''
CONVERGENCE ANALYSIS ON SINGLE INTEGRAL
'''
#looking at the ROC of our implementation of the trapezoid rule
#assume that with n=10000 trapezoids, we have our 'true' solution
#compare values at different n to this true value
val = linear_slope(10000)

error = []
solution = []

for i in range(1,101):
    v = linear_slope(i)
    error.append((abs((v-val)/val))*100)
    solution.append(v)

plt.plot(np.arange(1,101,1), solution, 'r')
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.title("Conversion of the trapezoid rule for single integral approximation")
plt.ylabel("Value of integral")
plt.xlabel("Number of trapezoids used")
plt.show()

plt.plot(np.arange(1,101,1), error, 'r')
plt.grid(color='k', linestyle='--', linewidth=0.5)
plt.title("Conversion of the error using trapezoid rule for single integral approximation")
plt.ylabel("Relative error (%)")
plt.xlabel("Number of trapezoids used")
plt.show()

#evaluate the rate of convergence of our implementation of the secant method
eps = np.arange(0.00001, 0.10001, 0.001)
nsteps = []

for e in eps:
    nsteps.append(secant(0,0.1,e)[1])

nsteps = np.array(nsteps)
plt.plot(nsteps, eps, 'r')
plt.grid(color='k', linestyle='dotted', linewidth=0.5)
plt.title("Rate of convergence of error for the secant method in 1D")
plt.ylabel("Error")
plt.xlabel("Number of steps to solve")
plt.show()


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

x1 = np.random.normal(loc=0.5, scale=1 , size =100).reshape(-1,1)
x2 = np.random.normal(loc=10, scale=2 , size =100).reshape(-1,1)
x3 = np.random.normal(loc=0, scale=3 , size =100).reshape(-1,1)
x = np.concatenate([x1 , x2 , x3])

plt.figure()
sns.distplot(x)
plt.title("Gaussian Mixture Model")

def Gaus(x, m, s): #正規分布の値を取得
    g = np.exp( - pow((x-m), 2) /(2*s) )/np.sqrt(2*np.pi * s )
    return g

def calc_gamma(x, pi, mu, sig):#事後分布の計算
    gam = pi*Gaus(x,mu,sig)
    gam/= np.sum(gam, axis=1).reshape(len(x),1)
    return gam

def update_parmas(gamma, x, pi, mu, sig):#パラメーターの更新式
    N_k = np.sum(gamma, axis=0)
    N = np.sum(N_k)
    mu_k = np.sum(x*gamma, axis=0 ) /N_k
    sig_k = np.sum(gamma* pow(x-mu, 2), axis=0) /N_k
    pi_k = N_k/N
    return pi_k , mu_k, sig_k

def iteration(x,mu,sig,pi, I=100, e=0.01):#ε以下になるか、100回計算するまで尤度を更新する関数
    LF=0
    for i in range(I):
        gamma = calc_gamma(x, pi, mu, sig)
        LF_new =np.sum(np.log(np.sum(pi*Gaus(x,mu,sig),axis=1 )) )
        ch = LF_new - LF
        print("LF ={} . change = {}".format(LF_new, ch))
        if np.abs(ch) < e:
            print("Iteration is finished {} iter. ".format(i+1))
            break
        LF=LF_new
        pi, mu, sig = update_parmas(gamma, x, pi, mu, sig)

    return pi, mu, sig

mu =np.array([0, 10, 3])
sig=np.array([1, 5, 10])
pi=np.array([0.1, 0.4, 0.5])

# execute
pi, mu, sig = iteration(x, mu, sig, pi)

# show results
print(pi, mu, np.sqrt(sig) )
y0 = np.random.normal(loc=mu[0], scale=np.sqrt(sig)[0] , size =int(300*pi[0]) ).reshape(-1,1)
y1 = np.random.normal(loc=mu[1], scale=np.sqrt(sig)[1] , size =int(300*pi[1]) ).reshape(-1,1)
y2 = np.random.normal(loc=mu[2], scale=np.sqrt(sig)[2] , size =int(300*pi[2]) ).reshape(-1,1)
y=np.concatenate([y0, y1, y2])

plt.figure()
sns.distplot(y)
plt.title("Predicted GMM")

plt.show()


from __future__ import division

import numpy as np

from scipy.special import digamma, polygamma

def get_noise_distribution(data, method='moments'):
    '''Computes sigma and N from an array of gamma distributed data

    input
    -----
    data
        A numpy array of gamma distributed values
    method='moments' or method='maxlk'
        Use either the moments or maximum likelihood equations to estimate the parameters.

    output
    ------
    sigma, N
        parameters related to the original Gaussian noise distribution
    '''

    data = data[data > 0]

    # If we have no voxel or only the same value
    # it leads to a divide by 0 as an edge case
    if data.size == 0 or np.std(data) == 0:
        return 0, 0

    # First get sigma
    if method == 'moments':
        mdata2 = np.mean(data**2)
        mdata4 = np.mean(data**4)

        p1 = mdata4 / mdata2
        p2 = mdata2
        sigma = np.sqrt(p1 - p2) / np.sqrt(2)
    elif method == 'maxlk':
            sigma = maxlk_sigma(data)
    else:
        raise ValueError('Invalid method name {}'.format(method))

    t = data**2 / (2*sigma**2)

    # Now compute N
    if method == 'moments':
        N = np.mean(t)
    elif method == 'maxlk':
            y = np.mean(np.log(t))
            N = inv_digamma(y)
    else:
        raise ValueError('Invalid method name {}'.format(method))

    return sigma, N

def maxlk_sigma(m, xold=None, eps=1e-8, max_iter=100):
    '''Maximum likelihood equation to estimate sigma from gamma distributed values'''

    sum_m2 = np.sum(m**2)
    K = m.size
    sum_log_m2 = np.sum(np.log(m**2))

    def f(sigma):
        return digamma(sum_m2/(2*K*sigma**2)) - sum_log_m2/K + np.log(2*sigma**2)

    def fprime(sigma):
        return -sum_m2 * polygamma(1, sum_m2/(2*K*sigma**2)) / (K*sigma**3) + 2/sigma

    if xold is None:
        xold = m.std()

    for _ in range(max_iter):

        xnew = xold - f(xold) / fprime(xold)

        if np.abs(xold - xnew) < eps:
            break

        xold = xnew

    return xnew

def inv_digamma(y, eps=1e-8, max_iter=100):
    '''Numerical inverse to the digamma function by root finding'''

    if y >= -2.22:
        xold = np.exp(y) + 0.5
    else:
        xold = -1 / (y - digamma(1))

    for _ in range(max_iter):

        xnew = xold - (digamma(xold) - y) / polygamma(1, xold)

        if np.abs(xold - xnew) < eps:
            break

        xold = xnew

    return xnew


import math
import numpy as np
import scipy
import networkx as nx
from manim import *

config.background_color = WHITE
#config.frame_width=4   # Enlarges everything, including the stroke width. Default: 128/9≈14.222

FG = BLACK

class Tree(Scene):
    def f(self, position = [0,0,0], direction = 0, span = 2*PI, degree=3, length=1):
        step = 2/degree
        start = -1 + step/2
        angles = [ start + k*step for k in range(degree) ]
        angles = [ span * a for a in angles ]
        angles = [ direction + a for a in angles ]
        xs = [ position[0] + length * math.cos(angle) for angle in angles ]
        ys = [ position[1] + length * math.sin(angle) for angle in angles ]
        points = [ [x,y,0] for x,y in zip(xs,ys) ]
        lines = [ Line(position,p).set_stroke(FG) for p in points ]
        return lines

    def g(self, lines, span, degree, length):
        new_lines = []
        for l in lines:
            position = l.end
            x1,y1,_ = l.start
            x2,y2,_ = l.end
            direction = math.atan2( y2-y1, x2-x1 )
            print( direction )
            new_lines += self.f(position, direction, span, degree, length)
        return new_lines

    def h(self, lines):
        self.play( Create( VGroup(*lines) ) )
        #for line in lines:
        #    self.add(line)

    def construct(self):
        lines = self.f(degree=5,length=.8)
        self.h(lines)
        self.wait(1)

        lines = self.g( lines, span = 2*PI / 7, degree=4, length=.8 )
        self.h(lines)
        self.wait(1)

        lines = self.g( lines, span = 2*PI / 12, degree=4, length=.6 )
        self.h(lines)
        self.wait(1)

        lines = self.g( lines, span = 2*PI / 15, degree=4, length=.3 )
        self.h(lines)
        self.wait(10)

class Circles(Scene):
    def construct(self):
        def f(R=1, n=5, offset=0):
            c = Circle().scale(R).set_stroke(GREY, width=10)
            self.play( Create(c) )

            angles = np.linspace(0,2*PI,n+1)[:-1] + offset
            dots = [ Dot([R * math.cos(a), R * math.sin(a),0]).set_color(FG).scale(2) for a in angles ]
            self.play( Create( VGroup(*dots) ) )
            self.wait(1)
            return dots

        def g(a,b):
            lines = []
            def d(u,v):
                dx = ( u.get_center()[0] - v.get_center()[0] ) **2
                dy = ( u.get_center()[1] - v.get_center()[1] ) **2
                dz = ( u.get_center()[2] - v.get_center()[2] ) **2
                return dx + dy + dz
            for u in b:
                print( f"{u.get_center()}" )
                dist = np.inf
                w = None
                for v in a:
                    if d(u,v) < dist:
                        w = v
                        dist = d(u,v)
                lines += Line( w.get_center(), u.get_center() ).set_stroke(FG)
            self.play( Create( VGroup(*lines) ) )
            self.wait(1)

        if True:

                k = 4
                a = [ Dot([0,0,0]).set_color(FG).scale(2) ]
                self.add( a[0] )
                self.wait(1)

                b = f(1,k,0)
                g(a,b)
                c = f(2,k*(k-1),0)
                g(b,c)
                d = f(3,k*(k-1)**2,0)
                g(c,d)
                e = f(4,k*(k-1)**3,0)
                g(d,e)

        text1 = MathTex( r"2\pi", "R" ).set_color(FG).shift(6*LEFT+3*UP)
        text2 = MathTex( r"2\pi", "n" ).set_color(FG).shift(6*LEFT+3*UP)
        self.play(Write(text1))
        self.wait(1)
        self.play(Transform(text1, text2))
        self.wait(1)

        text = MathTex( r"4\times3^{n-1}" ).set_color(FG).shift(6*LEFT+2*UP)
        self.play(Write(text))
        self.wait(1)

        self.wait(10)

class MST(Scene):
    def construct(self):

        def f(A):
            g = nx.Graph()
            for i in range(A.shape[0]):
                g.add_node(i)
            for i in range(A.shape[0]):
                for j in range(A.shape[1]):
                    if ( A[i,j] or A[j,i] ) and i<j:
                        g.add_edge(i,j)
            return g

        k = 100
        x = np.random.normal(size=(k,2*k))
        C = np.corrcoef(x)
        A = scipy.sparse.csgraph.minimum_spanning_tree(1-C)
        g = f(A)
        xy = nx.kamada_kawai_layout(g)
        xy = np.array(list(xy.values()))
        xy = xy - xy.min(axis=0)
        xy = xy / xy.max(axis=0)
        xy = 2 * xy - 1
        xy[:,0] = 6*xy[:,0]
        xy[:,1] = 3*xy[:,1]
        print(xy)
        objects = []
        for i in range(k):
            d = Dot( [ xy[i,0], xy[i,1], 0 ] ).set_color(FG).scale(1)
            objects += d
            for j in range(i):
                if A[i,j] == 0 and A[j,i] == 0:
                    continue
                line = Line( [ xy[i,0], xy[i,1], 0 ], [ xy[j,0], xy[j,1], 0 ] ).set_color(FG)
                objects += line
        self.play( Create(VGroup(*objects)) )

        self.wait(10)

from scipy.optimize import fsolve

def poincare_disk_compute_line( x1,y1, x2,y2 ):
    """
    Given two points (x1,y1), (x2,y2) in the unit disk,
    compute the parameters the circle going through them
    and orthogonal to the unit circle.

    I assume that the line is not a diameter: the circle would be at infinity

    Inputs:  x1,y1: first point (cartesian coordinates, i.e., x^2+y^2<=1)
             x2,y2: second point
    Outputs: x0,y0: center of the circle (outside the unit circle)
             r: radius
             theta1, theta2: angles at which the two points are
    """
    def f(x):
        a,b = x
        return np.array([
            x1**2 + y1**2 + a*x1 + b*y1 + 1,
            x2**2 + y2**2 + a*x2 + b*y2 + 1,
        ])
    x0 = np.random.uniform(-1,1,2)
    a,b = fsolve(f,x0)
    x0, y0 = -a/2, -b/2
    r = math.sqrt( a**2/4 + b**2/4 - 1 )
    theta1 = math.atan2( y1-y0, x1-x0 )
    theta2 = math.atan2( y2-y0, x2-x0 )
    if theta1 <= theta2:
        if theta2 - theta1 > PI:
            theta2 = theta2 - 2*PI
    else:
        if theta1 - theta2 > PI:
            theta1 = theta1 - 2 * PI
    return x0, y0, r, theta1, theta2

def poincare_disk_line(x0, y0, r, scale=1):
    return Circle().scale(r*scale).shift([x0*scale,y0*scale,0]).set_color(GREY)

def poincare_disk_segment(x0, y0, r, theta1, theta2, n=10, scale=1):
    ts = np.linspace( theta1, theta2, n )
    lines = []
    for t1,t2 in zip( ts[:-1], ts[1:] ):
        lines += Line(
            scale * np.array([ x0 + r * math.cos(t1), y0 + r*math.sin(t1), 0 ] ),
            scale * np.array([ x0 + r * math.cos(t2), y0 + r*math.sin(t2), 0 ] ),
        ).set_color(FG)
    return lines

class PoincareDisk(Scene):
    SCALE = 3
    def construct(self):
        c = Circle().scale(self.SCALE).set_color(FG)
        self.play( Create(c) )
        self.wait(1)

        def random_point():
            done = False
            while not done:
                x = np.random.uniform(-1,+1)
                y = np.random.uniform(-1,+1)
                done = x**2 + y**2 <= 1
            return x,y

        for _ in range(5):
            x1,y1 = random_point()
            x2,y2 = random_point()
            dots = [
                Dot( self.SCALE * np.array([x1,y1,0]) ).set_color(FG),
                Dot( self.SCALE * np.array([x2,y2,0]) ).set_color(FG)
            ]
            self.play( Create(VGroup(*dots)) )
            x0, y0, r, theta1, theta2 = poincare_disk_compute_line( x1,y1, x2,y2 )
            c = poincare_disk_line( x0, y0, r, scale=self.SCALE )
            self.play( Create(c) )
            lines = poincare_disk_segment(x0, y0, r, theta1, theta2, n=10, scale=self.SCALE)
            self.play( Create(VGroup(*lines)) )
            self.wait(1)

def poincare_half_plane_compute_line(x1,y1, x2,y2):
    def f(x):
        x0, r, theta1, theta2 = x
        return [
            x0 + r * math.cos(theta1) - x1,
            0  + r * math.sin(theta1) - y1,
            x0 + r * math.cos(theta2) - x2,
            0  + r * math.sin(theta2) - y2,
        ]
    code = 0
    while code != 1:
        x0 = np.array([
            np.random.uniform(x1,x2),
            np.random.uniform(0,3),   # TODO
            np.random.uniform(0,PI),
            np.random.uniform(0,PI),
        ])
        (x0, r, theta1, theta2), _, code, msg= fsolve(f,x0, full_output=True)

    theta1 = theta1 % TAU
    theta2 = theta2 % TAU
    print( f"x0={x0} r={r} theta1={theta1} theta2={theta2}" )
    return x0, r, theta1, theta2

def poincare_half_plane_line( x0, r, shift=0 ):
    return Arc( radius=r, start_angle = 1e-4, angle=PI-2e-4, arc_center=[x0,shift,0] ).set_color(GREY)  # There is also a num_component=9 argument

def poincare_half_plane_segment( x0, r, theta1, theta2, shift=0 ):
    if theta1 > theta2:
        theta1, theta2 = theta2, theta1
    return Arc( radius=r, start_angle=theta1, angle=theta2-theta1, arc_center=[x0,shift,0] ).set_color(FG)

class PoincareHalfPlane(Scene):
    SHIFT = -2.5
    def construct(self):
        l = Line( [-7,self.SHIFT,0], [7,self.SHIFT,0] ).set_color(FG)
        self.play( Create(l) )

        def random_point():
            return np.random.uniform(-6,6), np.random.uniform(0,3)

        for _ in range(5):
            x1,y1 = random_point()
            x2,y2 = random_point()
            dots = [
                Dot( [x1,y1+self.SHIFT,0] ).set_color(FG),
                Dot( [x2,y2+self.SHIFT,0] ).set_color(FG)
            ]
            self.play( Create(VGroup(*dots)) )
            x0, r, theta1, theta2 = poincare_half_plane_compute_line( x1,y1, x2,y2 )
            c = poincare_half_plane_line( x0, r, shift=self.SHIFT )
            self.play( Create(c) )
            lines = poincare_half_plane_segment(x0, r, theta1, theta2, shift=self.SHIFT)
            self.play( Create(VGroup(*lines)) )
            self.wait(1)

        self.wait(10)

class PoincareHalfPlane2(Scene):
    SHIFT = -2.5
    def construct(self):
        l = Line( [-7,self.SHIFT,0], [7,self.SHIFT,0] ).set_color(FG)
        self.play( Create(l) )

        def random_point():
            return np.random.uniform(-6,6), np.random.uniform(0,3)

        def f(x1,y1,x2,y2,run_time=.5):
            x0, r, theta1, theta2 = poincare_half_plane_compute_line( x1,y1, x2,y2 )
            lines = poincare_half_plane_segment(x0, r, theta1, theta2, shift=self.SHIFT)
            self.play( Create(VGroup(*lines)), run_time = run_time )

        x1,y1 = -3,1
        x2,y2 = +3,1
        dots = [
            Dot( [x1,y1+self.SHIFT,0] ).set_color(FG),
            Dot( [x2,y2+self.SHIFT,0] ).set_color(FG),
        ]
        self.play( Create( VGroup(*dots) ) )
        self.wait(1)
        f(x1,y1,x2,y2,1)
        self.wait(1)
        for x in [-6,-5,-4,-2,-1,0,1,2,4,5,6]:
            f(x1,y1,x,1e-4)
        self.play( Create( Line( [x1,self.SHIFT,0], [x1,6,0] ).set_color(FG) ) )
        for x in [10,-10,20,-20,30,-30,-50]:
            f(x1,y1, x,1e-4, .1)
        self.wait(10)

import numpy,import scipy,from scipy,import networkx


import numpy as np
from scipy import optimize
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

A = np.array([
    [2, 1],     # 2*x0 + x1 <= 6
    [-1, -1]    # -x0 - x1 <= -4
])
b = np.array([6, -4])

x0_bounds = (-3, 14)  # -3 <= x0 <= 14
x1_bounds = (2, 12)   #  2 <= x1 <= 12

c = np.array([1, 5])

def func(x):
    return np.tensordot(c, x, axes=1)

fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.set(xlabel="x0", ylabel="x1", zlabel="func")
ax.set_title("Values in feasible region")

X0 = np.linspace(*x0_bounds)
X1 = np.linspace(*x1_bounds)
x0, x1 = np.meshgrid(X0, X1)
z = func([x0, x1])

ax.plot_surface(x0, x1, z, alpha=0.3)

Y = (b[0] - A[0, 0]*X0) / A[0, 1]
I = np.logical_and(Y >= x1_bounds[0], Y <= x1_bounds[1])
ax.plot(X0[I], Y[I], func([X0[I], Y[I]]), "r", lw=1.5)

Y = (b[1] - A[1, 0]*X0) / A[1, 1]
I = np.logical_and(Y >= x1_bounds[0], Y <= x1_bounds[1])
ax.plot(X0[I], Y[I], func([X0[I], Y[I]]), "r", lw=1.5)

B = np.tensordot(A, np.array([x0, x1]), axes=1)
II = np.logical_and(B[0, ...] <= b[0], B[1, ...] <= b[1])
ax.plot_trisurf(x0[II], x1[II], z[II], color="b", alpha=0.5)

res = optimize.linprog(c, A_ub=A, b_ub=b, bounds=(x0_bounds, x1_bounds))
print(res)

ax.plot([res.x[0]], [res.x[1]], [res.fun], "k*")

plt.show()


import numpy as np
import cv2

def pos_int(p):
    return (int(p[0]), int(p[1]))

def cubic_spline(x,y,interval=2):
    size = len(y)
    h = [x[i+1]-x[i] for i in range(len(x)-1)]
    A = np.zeros((size, size), dtype=np.float)
    for i in range(size):
        if i==0:
            A[i,0] = 1
            #A[i,0] = -h[i+1]
            #A[i,1] = h[i+1] + h[i]
            #A[i,2] = -h[i]
        elif i==size-1:
            A[i,-1] = 1
            #A[i,-3] = -h[i-1]
            #A[i,-2] = h[i-2] + h[i-1]
            #A[i,-1] = -h[i-1]
        else:
            A[i,i-1] = h[i-1]
            A[i,i] = 2*(h[i-1]+h[i])
            A[i,i+1] = h[i]
    #print(A)

    B = np.zeros((size,1), dtype=np.float)
    for i in range(1,size-1):
        B[i,0] = (y[i+1]-y[i])/h[i] - (y[i]-y[i-1])/h[i-1]
    B = 6*B
    #print(B)

    Ainv = np.linalg.pinv(A)
    m = Ainv.dot(B).T[0].tolist()
    a = [y[i] for i in range(size-1)]
    b = [(y[i+1]-y[i])/h[i] - h[i]*m[i]/2 - h[i]*(m[i+1]-m[i])/6 for i in range(size-1)]
    c = [m[i]/2 for i in range(size-1)]
    d = [(m[i+1]-m[i])/(6*h[i]) for i in range(size-1)]

    y_list = []
    dy_list = []
    ddy_list = []
    i = 0
    x_ = x[0]
    while(True):
        if x_ >= x[-1]:
            x_ = x[-1]
        elif x_ > x[i+1]:
            i += 1
        y_ = a[i] + b[i]*(x_-x[i]) + c[i]*(x_-x[i])**2 + d[i]*(x_-x[i])**3
        dy = b[i] + 2.0*c[i]*(x_-x[i]) + 3.0*d[i]*(x_-x[i])**2
        ddy = 2.0*c[i] + 6.0*d[i]*(x_-x[i])
        y_list.append(y_)
        dy_list.append(dy)
        ddy_list.append(ddy)
        if x_ == x[-1]:
            break
        x_ += interval
    return y_list, dy_list, ddy_list

def cubic_spline_2d(path, interval=2):
    x = [path[i][0] for i in range(len(path))]
    y = [path[i][1] for i in range(len(path))]
    x_diff = [path[i+1][0]-path[i][0] for i in range(len(path)-1)]
    y_diff = [path[i+1][1]-path[i][1] for i in range(len(path)-1)]
    dist = np.hypot(np.array(x_diff),np.array(y_diff))
    dist_cum = np.cumsum(dist).tolist()
    dist_cum.insert(0,0)
    #print(dist, dist_cum)

    x_list, dx_list, ddx_list = cubic_spline(dist_cum,x,interval)
    y_list, dy_list, ddy_list = cubic_spline(dist_cum,y,interval)
    dx, ddx, dy, ddy = np.array(dx_list), np.array(ddx_list), np.array(dy_list), np.array(ddy_list)
    yaw_list = np.rad2deg(np.arctan2(dy,dx))
    curv_list = (ddy * dx - ddx * dy) / ((dx ** 2 + dy ** 2)**(3 / 2))
    path_smooth = [(x_list[i], y_list[i], yaw_list[i], curv_list[i]) for i in range(len(x_list))]

    #print(path_smooth)
    return path_smooth

if __name__ == "__main__":
    path = [(20,50), (40,100), (80,120), (160,60)]
    path_smooth = cubic_spline_2d(path)

    img = np.ones((200,200,3), dtype=np.float)
    for p in path:
        cv2.circle(img, p, 3, (0.5,0.5,0.5), 2)

    for i in range(len(path_smooth)-1):
        cv2.line(img, pos_int(path_smooth[i]), pos_int(path_smooth[i+1]), (1.0,0.4,0.4), 1)

    img = cv2.flip(img,0)
    cv2.imshow("Cubic Spline Test", img)
    cv2.waitKey(0)


#!/usr/bin/env python
"""ascention.py: Python からはじめる数学入門 pp. 209-210, 213-214.

Usage:
  ascention.py
"""
from sympy import sympify, Symbol, diff, solve

class GradientAscentError(Exception): pass

def grad_ascent(x0, f1x, x):
    """Gradient ascent to find the maximum value of a single
    variable function
    """

    if not solve(f1x):
        raise GradientAscentError(
            f'Cannot continue, solution for {f1x} = 0 does not exist')

    eps = 1e-6
    step_size = 1e-4
    x_old = x0
    x_new = x_old + step_size * f1x.subs({x: x_old}).evalf()
    while abs(x_old - x_new) > eps:
        x_old = x_new
        x_new = x_old + step_size * f1x.subs({x: x_old}).evalf()

    return x_new

def main():
    f = input('Enter a function in one variable: ')
    v = input('Enter the variable: ')
    init = float(input('Enter the initial value of the variable: '))
    f = sympify(f)

    v = Symbol(v)
    d = diff(f, v)
    try:
        vmax = grad_ascent(init, d, v)
        print(f'{v.name}: {vmax}')
        print(f'Maximum value: {f.subs({v: vmax})}')
    except GradientAscentError as exc:
        print(exc)

if __name__ == '__main__':
    main()


import numpy as np

def func_levy(params, diff):
    """Levy function N13, which is:
    f(x) = sin^{2}(3\pi x_{1}) + (x_{1}-1)^{2}[1+sin^{2}(c \pi x_2)]
             + (x_2-1)^{2}[1+sin^{2}(2 \pi x_2)]]
    link to the function: https://tinyurl.com/tfn3o7p

    Args:
        params(np.array): 1d numpy array of function arguments
        diff(float): difficulty parameter, controls wiggliness of the function

    Returns:
        func(float): value of the Levy function evaluated at `params` and `diff`

    """
    param1 = params[0]
    param2 = params[1]
    func = (
        (np.sin(3 * np.pi * param1)) ** 2
        + (param1 - 1) ** 2 * (1 + np.sin(diff * param2) ** 2)
        + (param2 - 1) ** 2 * (1 + np.sin(2 * np.pi * param2) ** 2)
    )
    return func

def grad_levy(params, diff):
    """Gradient of Levy functio N13.

    Args:
        params(np.array): 1d numpy array of function arguments
        diff(float): difficulty parameter, controls wiggliness of the function

    Returns:
        grad(np.array): 1d numpy array of Levy function derivative for each
            argument, evaluated at `params` and `diff`

    """
    param1 = params[0]
    param2 = params[1]
    der_param1 = 6 * np.pi * np.cos(3 * np.pi * param1) * np.sin(
        3 * np.pi * param1
    ) + 2 * ((np.sin(diff * param2)) ** 2 + 1) * (param1 - 1)
    der_param2 = (
        2 * diff * (param1 - 1) ** 2 * np.cos(diff * param2) * np.sin(diff * param2)
        + 2 * (param2 - 1) * ((np.sin(2 * np.pi * param2)) ** 2 + 1)
        + 4
        * np.pi
        * (param2 - 1) ** 2
        * np.cos(2 * np.pi * param2)
        * np.sin(2 * np.pi * param2)
    )

    grad = np.array([der_param1, der_param2])
    return grad

def hessian_levy(params, diff):
    """Hessian matrix of Levy function.

    Args:
        params(np.array): 1d array of function arguments
        diff(float): difficulty parameter, controls wiggliness of the function

    Returns:
        hess_mat(np.array): 2d numpy array with the Levy function hessian,
            evaluated at `params` and `diff`

    """
    param1 = params[0]
    param2 = params[1]

    der_param1_param1 = (
        -18 * np.pi ** 2 * (np.sin(3 * np.pi * param1)) ** 2
        + 18 * np.pi ** 2 * (np.cos(3 * np.pi * param1)) ** 2
        + 2 * ((np.sin(diff * param2)) ** 2 + 1)
    )
    der_param1_param2 = (
        4 * diff * (param1 - 1) * np.cos(diff * param2) * np.sin(diff * param2)
    )
    der_param2_param1 = (
        4 * diff * (param1 - 1) * np.cos(diff * param2) * np.sin(diff * param2)
    )
    der_param2_param2 = (
        2 * diff ** 2 * (param1 - 1) ** 2 * np.cos(2 * diff * param2)
        + 8 * np.pi * (param2 - 1) * np.sin(4 * np.pi * param2)
        + (8 * np.pi ** 2 * (param2 - 1) ** 2 - 1) * np.cos(4 * np.pi * param2)
        + 3
    )

    hess_mat =  np.array(
        [[der_param1_param1, der_param1_param2], [der_param2_param1, der_param2_param2]]
    )
    return hess_mat


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

# code starts here
df = pd.read_csv(path)
#print(df)

p_a = len(df[df['fico']> 700])/len(df)
print(p_a)

p_b = len(df[df['purpose']== 'debt_consolidation'])/len(df)
print(p_b)

df1 = df[df['purpose']== 'debt_consolidation']

p_b_a = len(df1[df1['fico']> 700])/len(df1)

print(p_b_a)

p_a_b = (p_b_a * p_b)/p_a
print(p_a_b)

result = p_b_a == p_a
# code ends here

# --------------
# code starts here
prob_lp = len(df[df['paid.back.loan']=='Yes'])/len(df)
print(prob_lp)

prob_cs = len(df[df['credit.policy']=='Yes'])/len(df)
print(prob_cs)

new_df = df[df['paid.back.loan']=='Yes']

prob_pd_cs = len(new_df[new_df['credit.policy'] == 'Yes'])/len(new_df)
print(prob_pd_cs)

bayes = (prob_pd_cs * prob_lp)/prob_cs
print(bayes)
# code ends here

# --------------
# code starts here
import matplotlib.pyplot as plt

df['purpose'].value_counts().plot(kind = 'bar')

plt.show()

df1 = df[df['paid.back.loan']=='No']

df1['purpose'].value_counts().plot(kind = 'bar')

plt.show()
# code ends here

# --------------
# code starts here

inst_median = df['installment'].median()

inst_mean = df['installment'].mean()

df['installment'].plot(kind = 'hist')

plt.show()

df['log.annual.inc'].plot(kind = 'hist')

plt.show()
# code ends here


from math import cos,sin,sqrt,atan2,pi,exp
import numpy as np
# define parameters
alpha1=0.1
alpha2=0.12
alpha3=0.25
alpha4=0.2
alpha5=0.13
alpha6=0.16

def motion_model_velocity(xt, ut, xt_1, prob):
    # xt = [x_dash y_dash theta_dash]  , xt_1 = [x y z], ut=[ut[0]w]
    mu=0.5*((xt_1[0]-xt[0])*cos(xt_1[2])+(xt_1[1]-xt[1])*sin(xt_1[2])) / \
    ((xt_1[1]-xt[1])*cos(xt_1[2])+(xt_1[0]-xt[0])*sin(xt_1[2]))

    xstar=(xt_1[0]+xt[0])/2+mu*(xt_1[1]-xt[1])
    ystar=(xt_1[1]+xt[1])/2+mu*(xt[0]-xt_1[0])
    rstar=sqrt((xt_1[0]-xstar)**2+(xt_1[1]-ystar)**2)

    dtheta=atan2(xt[1]-ystar, xt[0]-xstar)-atan2(xt_1[1]-ystar, xt_1[0]-xstar)

    v_hat=dtheta/dt*rstar
    w_hat=dtheta/dt
    gamma=(xt[2]-xt_1[2])/2-w_hat

    return prob(ut[0]-v_hat, alpha1*abs(ut[0])+alpha2*abs(ut[1]))*prob(ut[1]-w_hat, alpha3*abs(ut[0])+\
                alpha4*abs(ut[1]))*prob(gamma,alpha5*abs(ut[0])+alpha6*abs(ut[1]))

def normal_prob(a, b):
    return 1/sqrt(2*pi*b) * exp(-0.5*a**2/b)

def tri_prob(a,b):
    if abs(a)>sqrt(6*b) :
        return 0
    else :
        return (sqrt(6*b)-abs(a))/(6*b)

def sample_v_model(ut, xt_1, sample):
    v_hat=u[0]+sample(alpha1*abs(u[0])+alpha2*abs(u[1]))
    w_hat=u[1]+sample(alpha3*abs(u[0])+alpha4*abs(u[1]))
    gamma=sample(alpha5*abs(u[0])+alpha6*abs(u[1]))

    xt=np.matrix(np.zeros((3,1)))
    xt[0,0]=xt_1[0]-v_hat/w_hat*sin(xt_1[2])+v_hat/w_hat*sin(xt_1[2]+w_hat*dt)
    xt[1,0]=xt_1[1]+v_hat/w_hat*cos(xt_1[2])-v_hat/w_hat*cos(xt_1[2]+w_hat*dt)
    xt[2,0]=xt_1[2]+w_hat*dt+gamma*dt

    return xt

def normal_sample(b):
    ret=0
    for i in range(1,13):
        ret+=np.random.uniform(-1,1)
    return ret*b/6

def tri_sample(b):
    return b*np.random.uniform(-1,1)*np.random.uniform(-1,1)

################# test ###############################


import numpy as np

def PCA_numpy(data, n_components=2):

    #1nd step is to find covarience matrix
    data_vector = []
    for i in range(data.shape[1]):
        data_vector.append(data[:, i])

    cov_matrix = np.cov(data_vector)

    #2rd step is to compute eigen vectors and eigne values
    eig_values, eig_vectors = np.linalg.eig(cov_matrix)
    eig_values = np.reshape(eig_values, (len(cov_matrix), 1))

    #Make pairs
    eig_pairs = []
    for i in range(len(eig_values)):
         eig_pairs.append([np.abs(eig_values[i]), eig_vectors[:,i]])

    eig_pairs.sort()
    eig_pairs.reverse()

    #This PCA is only for 2 components
    reduced_data = np.hstack((eig_pairs[0][1].reshape(len(eig_pairs[0][1]),1), eig_pairs[1][1].reshape(len(eig_pairs[0][1]),1)))

    return data.dot(reduced_data)


import pandas as pd
import numpy as np
from pandas_datareader import data
import yaml
import sys
import math
sys.setrecursionlimit(20000)

def SharpeRatio(
    returns : np.array) -> float:
    return np.mean(returns, axis=0) / (np.std(returns, axis=0))

def hit_ratio(
    returns : np.array) -> float:
    return np.sum(returns > 0, axis=0) /len(returns)

def featureNormalizer(X):
	m = len(X)
	mean = np.mean(X)
	std = np.std(X)
	X_norm = (X-mean)/std
	return X_norm , mean , std

def getcolumn(
    df: pd.DataFrame,
    column_name: str)-> np.array:
    return df[column_name].to_numpy()

def LoadConfig(
    yamlpath: str)-> dict:
    config = yaml.load(
        open(yamlpath, 'r'),
        Loader=yaml.FullLoader)
    return config

def GetData(
    ticker : str,
    start_date : str,
    end_date : str)-> pd.DataFrame:
    """Getting historic price data from yahoo finance.

    Arguments:
        ticker {str}
        start_date {str}
        end_date {str}

    Returns:
        pd.DataFrame --> the output price dataframe
    """
    return data.DataReader(ticker,'yahoo', start_date, end_date)

def DSR(
    returns: np.array,
    t:int,
    decay=0.5
    )->float:
    a = ema_mean(returns,decay,t)
    b = ema_std(returns,decay,t)
    delta_a = ema_mean(returns, decay,t-1) - a
    delta_b = ema_mean(returns, decay,t-1) - b
    if a**2 > b:
        return (b * delta_a - 0.5 *a *delta_b)/  ( - (abs(b-a**2))**(1.5))

    else:
        delta_a = ema_mean(returns, decay,t-1) - a
        delta_b = ema_mean(returns, decay,t-1) - b
        return (b * delta_a - 0.5 *a *delta_b)/ ((b-a**2)**(1.5))

def dDSRdR(
    returns: np.array,
    t : int,
    decay=0.5)->float:
    a = ema_mean(returns,decay,t-1)
    b = ema_std(returns,decay,t-1)
    if a**2 > b:
        return (b - a * returns[t]) / (- abs((b-a**2))**(1.5))
    else:
        return (b - a * returns[t]) / ((b-a**2)**(1.5))

def ema_mean(
returns: np.array,
decay: float,
t: int)-> float :
    if t < 0:
        return returns[t]
    else:
        ema = returns[t-1] + decay*(
            returns[t]- ema_mean(
                returns,decay,t-1
                )
                )
    return ema

def ema_std(
returns: np.array,
decay: float,
t: int)-> float:
    if t < 0:
        return 0
    elif t==1:
        return returns[t]
    else:
        ema = returns[t-1] + decay*(
            returns[t]**2- ema_mean(
                returns,decay,t-1
                )
                )
    return ema


from sympy.matrices.expressions import MatrixExpr
from sympy import S, I, sqrt, exp

class DFT(MatrixExpr):
    """ Discrete Fourier Transform """
    n = property(lambda self: self.args[0])
    shape = property(lambda self: (self.n, self.n))

    def _entry(self, i, j):
        w = exp(-2*S.Pi*I/self.n)
        return w**(i*j) / sqrt(self.n)

    def _eval_inverse(self):
        return IDFT(self.n)

class IDFT(DFT):
    """ Inverse Discrete Fourier Transform """
    def _entry(self, i, j):
        w = exp(-2*S.Pi*I/self.n)
        return w**(-i*j) / sqrt(self.n)

    def _eval_inverse(self):
        return DFT(self.n)


import numpy as cp

class Tanh():
    '''
    The tanh function is just another possible functions that can be used as a nonlinear activation function between layers of a neural network.
    Tanh will map values to be between -1 and 1.
    the tanh function is that the derivative can be expressed in terms of the function itself.
    The logistic Tanh function is defined as follows:

        T(x) =   _exp(x)-exp(-x)_ where exp is exponential
                  exp(x)+exp(-x)
    '''
    def __call__(self, x: cp.array) -> cp.array:

        return cp.exp(x)-cp.exp(-x))/(cp.exp(x)+cp.exp(-x))

    def gradient(self, x: cp.array) -> cp.array:
        '''

        Parameters
        ----------
        x : cp.array
             Array of ouputs of deeplearning layer..

        Returns
        -------
        cp.array
            Array after derivatives of sigmoid applied .

        '''
        return (1 - self.__call__(x) ** 2)

tutorial/7. Deep Learning/Activation Function/tanh.py


import math
import json
import scipy.stats

def pearson(vector1, vector2):
    n = len(vector1)
    # simple sums
    sum1 = sum(float(vector1[i]) for i in range(n))
    sum2 = sum(float(vector2[i]) for i in range(n))
    # sum up the squares
    sum1_pow = sum([pow(v, 2.0) for v in vector1])
    sum2_pow = sum([pow(v, 2.0) for v in vector2])
    # sum up the products
    p_sum = sum([vector1[i]*vector2[i] for i in range(n)])
    # 分子num，分母den
    num = p_sum - (sum1*sum2/n)
    den = math.sqrt((sum1_pow-pow(sum1, 2)/n)*(sum2_pow-pow(sum2, 2)/n))
    if den == 0:
        return 0.0
    return num/den

def kendalltau(vector1, vector2):
    tau, p_value = scipy.stats.kendalltau(vector1, vector2)
    return tau

if __name__ == "__main__":
    json1_path = "eval/eval-final.json"
    json_target = "data/benchmark.json"

    f1 = open(json1_path, "r")
    f2 = open(json_target, "r")

    f1_dict = json.load(f1)
    f2_dict = json.load(f2)

    f1_list = []
    f2_list = []

    for k, v in f1_dict.items():
        f1_list.append(v["acc"])

    for k, v in f2_dict.items():
        f2_list.append(float(v["acc"]))

    print(f1_list, f2_list)

    print("person:", pearson(f1_list, f2_list))
    print("kendall", kendalltau(f1_list, f2_list))

    f1.close()
    f2.close()


import numpy as np

from fastats.linear_algebra.det import det
from fastats.linear_algebra.matrix_minor import matrix_minor

def inv(A):
    """
    Returns the inverse of A using the adjoint method.

    >>> import numpy as np
    >>> A = np.array([[4, 3], [3, 2]])
    >>> A_inv = inv(A)
    >>> A_inv
    array([[-2.,  3.],
           [ 3., -4.]])
    """
    m, n = A.shape
    co_factors = np.empty_like(A, dtype=np.float64)

    for i in range(n):
        for j in range(m):
            minor = matrix_minor(A, i, j)
            co_factors[i, j] = ((-1) ** (i + j)) * det(minor)

    adjoint = co_factors.T
    return adjoint / det(A)

if __name__ == '__main__':
    import pytest
    pytest.main([__file__])