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

#import ponio
import os,sys
import argparse

import jinja2 as jinja
import pickle

import numpy as np
import sympy as sp
from sympy import I

import ponio.runge_kutta as rk
import ponio.utils as utils

def runge_kutta_list () :
  """
    Function return list of Runge Kutta methods
    All Butcher tableaus are write as :
    c | A
    ------
      | b
  """
  rk_list = []

  """
    # Runge-Kutta methods order 1
  """

  A = [[0]]
  b = [1]
  c = [0]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="Euler method") )
  del A,b,c

  alpha = sp.Rational(3,4)
  A = [[0,0],
       [alpha,0]]
  b = [0,1]
  c = [0,alpha]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK NSSP(2,1)") )
  del alpha
  del A,b,c

  A = [[0,0],
       [1,0]]
  b = [sp.Rational(1,2),sp.Rational(1,2)]
  c = [0,sp.Rational(1,2)]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (2,1)") )
  del A,b,c

  """
    # Runge-Kutta methods order 2
  """

  alpha = sp.Rational(1,2)
  A = [[0,0],
       [alpha,0]]
  b = [1-sp.Rational(1,2*alpha),sp.Rational(1,2*alpha)]
  c = [0,alpha]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (2,2) mid-point rule") )
  del alpha
  del A,b,c

  alpha = 1
  A = [[0,0],
       [alpha,0]]
  b = [1-sp.Rational(1,2*alpha),sp.Rational(1,2*alpha)]
  c = [0,alpha]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (2,2) trapezoidal rule") )
  del alpha
  del A,b,c

  A = [[ 0                , 0                , 0 ],
       [ sp.Rational(1,2) , 0                , 0 ],
       [ sp.Rational(1,2) , sp.Rational(1,2) , 0 ]]
  b =  [ sp.Rational(1,3) , sp.Rational(1,3) , sp.Rational(1,3) ]
  c =  [ 0 , sp.Rational(1,2) , 1 ]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK SSP(3,2)") )
  del A,b,c

  A = [[ 0                , 0 , 0 ],
       [ sp.Rational(1,3) , 0 , 0 ],
       [ 0                , 1 , 0 ]]
  b =  [ sp.Rational(1,2) , 0 , sp.Rational(1,2) ]
  c =  [ 0 , sp.Rational(1,3) , 1 ]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK NSSP(3,2)") )
  del A,b,c

  A = [[0,0,0],
       [sp.Rational(1,2),0,0],
       [0,sp.Rational(1,2),0]]
  b = [0,0,1]
  c = [0,sp.Rational(1,2),sp.Rational(1,2)]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (3,2) best") )
  del A,b,c

  """
    # Runge-Kutta methods order 3
  """

  A = [[0,0,0],
       [1,0,0],
       [sp.Rational(1,4),sp.Rational(1,4),0]]
  b = [sp.Rational(1,6),sp.Rational(4,6),sp.Rational(1,6)]
  c = [0,sp.Rational(1,2),1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK SSP (3,3)") )
  del A,b,c

  A = [[0,0,0],
       [sp.Rational(1,2),0,0],
       [-1,2,0]]
  b = [sp.Rational(1,6),sp.Rational(4,6),sp.Rational(1,6)]
  c = [0,sp.Rational(1,2),1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (3,3)") )
  del A,b,c

  A = [[0,0,0],
       [-sp.Rational(4,9),0,0],
       [sp.Rational(7,6),-sp.Rational(1,2),0]]
  b = [sp.Rational(1,4),0,sp.Rational(3,4)]
  c = [0,-sp.Rational(4,9),sp.Rational(2,3)]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK NSSP (3,3)") )
  del A,b,c

  A = [[ 0 , 0 , 0 , 0 , 0 ]                ,
       [ sp.Rational(1,7)  , 0 , 0 , 0 , 0 ],
       [ 0 , sp.Rational(3,16) , 0 , 0 , 0 ],
       [ 0 , 0 , sp.Rational(1,3)  , 0 , 0 ],
       [ 0 , 0 , 0 , sp.Rational(2,3)  , 0 ]]
  b = [sp.Rational(1,4),0,0,0,sp.Rational(3,4)]
  c = [0,sp.Rational(1,7),sp.Rational(3,16),sp.Rational(1,3),sp.Rational(2,3)]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK NSSP (5,3)") )
  del A,b,c

  A = [[0,0,0],
       [sp.Rational(2,3),0,0],
       [sp.Rational(1,3),sp.Rational(1,3),0]]
  b = [sp.Rational(1,4),0,sp.Rational(3,4)]
  c = [0,sp.Rational(2,3),sp.Rational(2,3)]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (3,3) (233e)") )
  del A,b,c

  """
    # Runge-Kutta methods order 4
  """

  A = [[ 0               , 0               , 0 , 0 ],
       [ sp.Rational(1,2), 0               , 0 , 0 ],
       [ 0               , sp.Rational(1,2), 0 , 0 ],
       [ 0               , 0               , 1 , 0 ]]
  b = [sp.Rational(1,6),sp.Rational(1,3),sp.Rational(1,3),sp.Rational(1,6)]
  c = [0,sp.Rational(1,3),sp.Rational(2,3),1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (4,4)") )
  del A,b,c

  A = [[ 0               , 0 , 0 , 0 ],
       [ sp.Rational(1,3), 0 , 0 , 0 ],
       [-sp.Rational(1,3), 1 , 0 , 0 ],
       [ 1               ,-1 , 1 , 0 ]]
  b = [sp.Rational(1,8),sp.Rational(3,8),sp.Rational(3,8),sp.Rational(1,8)]
  c = [0,sp.Rational(1,3),sp.Rational(2,3),1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (4,4) 3/8-rule") )
  del A,b,c

  A = [[0,0,0,0],
       [sp.Rational(1,4),0,0,0],
       [0,sp.Rational(1,2),0,0],
       [1,-2,2,0]]
  b = [sp.Rational(1,6),0,sp.Rational(2,3),sp.Rational(1,6)]
  c = [0,sp.Rational(1,4),sp.Rational(1,2),1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (4,4) (235j)") )
  del A,b,c

  """
    # Runge-Kutta methods order 5
  """

  A = [[ 0                 , 0                , 0                , 0                 , 0                , 0 ],
       [ sp.Rational(1,4)  , 0                , 0                , 0                 , 0                , 0 ],
       [ sp.Rational(1,8)  , sp.Rational(1,8) , 0                , 0                 , 0                , 0 ],
       [ 0                 , 0                , sp.Rational(1,2) , 0                 , 0                , 0 ],
       [ sp.Rational(3,16) ,-sp.Rational(3,8) , sp.Rational(3,8) , sp.Rational(9,16) , 0                , 0 ],
       [-sp.Rational(3,7)  , sp.Rational(8,7) , sp.Rational(6,7) ,-sp.Rational(12,7) , sp.Rational(8,7) , 0 ]]
  b =  [ sp.Rational(7,90) , 0                , sp.Rational(32,90) , sp.Rational(12,90) , sp.Rational(32,90) , sp.Rational(7,90) ]
  c = [0,sp.Rational(1,4),sp.Rational(1,4),sp.Rational(1,2),sp.Rational(3,4),1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (6,5) (236a)") )
  del A,b,c

  A = [[0                      , 0                      ,0                      , 0                   , 0                      ,0                 ,0],
       [sp.Rational(1,5)       , 0                      ,0                      , 0                   , 0                      ,0                 ,0],
       [sp.Rational(3,40)      , sp.Rational(9,40)      ,0                      , 0                   , 0                      ,0                 ,0],
       [sp.Rational(44,45)     ,-sp.Rational(56,15)     ,sp.Rational(32,9)      , 0                   , 0                      ,0                 ,0],
       [sp.Rational(19372,6561),-sp.Rational(25360,2187),sp.Rational(64448,6561),-sp.Rational(212,729), 0                      ,0                 ,0],
       [sp.Rational(9017,3168) ,-sp.Rational(355,33)    ,sp.Rational(46732,5247), sp.Rational(49,176) ,-sp.Rational(5103,18656),0                 ,0],
       [sp.Rational(35,384)    , 0                      ,sp.Rational(500,1113)  , sp.Rational(125,192),-sp.Rational(2187,6784) ,sp.Rational(11,84),0]]
  b =  [sp.Rational(35,384)    , 0                      ,sp.Rational(500,1113)  , sp.Rational(125,192),-sp.Rational(2187,6784) ,sp.Rational(11,84),0]
  c = [0,sp.Rational(1,5),sp.Rational(3,10),sp.Rational(4,5),sp.Rational(8,9),1,1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="DP 5") )
  del A,b,c

  """
    # Runge-Kutta methods order 6
  """

  A = [[ 0                           , 0                        , 0                         , 0                          , 0                     ,0                    ,0                 ,0],
       [ sp.Rational(1,9)            , 0                        , 0                         , 0                          , 0                     ,0                    ,0                 ,0],
       [ sp.Rational(1,24)           , sp.Rational(1,8)         , 0                         , 0                          , 0                     ,0                    ,0                 ,0],
       [ sp.Rational(1,6)            ,-sp.Rational(1,2)         , sp.Rational(2,3)          , 0                          , 0                     ,0                    ,0                 ,0],
       [ sp.Rational(935,2536)       ,-sp.Rational(2781,2536)   , sp.Rational(309,317)      , sp.Rational(321,1268)      , 0                     ,0                    ,0                 ,0],
       [-sp.Rational(12710,951)      , sp.Rational(8287,317)    ,-sp.Rational(40,317)       ,-sp.Rational(6335,317)      , 8                     ,0                    ,0                 ,0],
       [ sp.Rational(5840285,3104064),-sp.Rational(7019,2536)   ,-sp.Rational(52213,86224)  , sp.Rational(1278709,517344),-sp.Rational(433,2448) ,sp.Rational(33,1088) ,0                 ,0],
       [-sp.Rational(5101675,1767592), sp.Rational(112077,25994), sp.Rational(334875,441898),-sp.Rational(973617,883796) ,-sp.Rational(1421,1394),sp.Rational(333,5576),sp.Rational(36,41),0]]
  b =  [sp.Rational(41,840),0,sp.Rational(9,35),sp.Rational(9,280),sp.Rational(34,105),sp.Rational(9,280),sp.Rational(9,35),sp.Rational(41,840)]
  c = [0,sp.Rational(1,9),sp.Rational(1,6),sp.Rational(1,3),sp.Rational(1,2),sp.Rational(2,3),sp.Rational(5,6),1]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK (8,6)") )
  del A,b,c

  A = [[  0.0                        ,  0.0                        ,   0.0                        ,  0.0                        ,  0.0                        ,  0.0                        , 0.0 ],
       [  0.202276644898140634933337 ,  0.0                        ,   0.0                        ,  0.0                        ,  0.0                        ,  0.0                        , 0.0 ],
       [  0.075853741836802738100001 ,  0.227561225510408214300004 ,   0.0                        ,  0.0                        ,  0.0                        ,  0.0                        , 0.0 ],
       [  1.359282217283300317252891 , -5.237885702628806615657060 ,   4.753603485345506298404170 ,  0.0                        ,  0.0                        ,  0.0                        , 0.0 ],
       [ -0.321092002258021684715280 ,  1.651353127922382381290896 ,  -0.905286676763720493279991 ,  0.075025551099359796704375 ,  0.0                        ,  0.0                        , 0.0 ],
       [  0.292321839349363565719798 , -0.748269386089829516522437 ,   0.592470844966485039986419 , -0.039554538849143620302490 ,  0.028031240623124531118711 ,  0.0                        , 0.0 ],
       [-20.662761894904085188637368 , 63.852320946332118743247958 , -74.151750947688834248615863 ,  0.864117644373384395219349 , 14.505481659294823706193336 , 16.592592592592592592592588 , 0.0 ]]
  b =  [  0.014285714285714285714286 ,  0.0                        ,   0.0                        ,  0.270899470899470899470899 ,  0.429629629629629629629630 ,  0.270899470899470899470899 , 0.014285714285714285714286 ]
  c = [ 0.0 , 0.202276644898140634933337 , 0.303414967347210952400006 , 0.875 , 0.5 , 0.125 , 1.0 ]
  rk_list.append( rk.butcher(A=A,b=b,c=c,label="RK6ES") )
  del A,b,c

  # end function
  return rk_list

def output_pickle (filename) :
    """
      Export list of Runge-Kutta methods as a pickle python file
    """
    if filename == None :
      filename = "rk_list.pickle"

    rk_list = runge_kutta_list()
    with open(filename,'wb') as f :
      pickle.dump(rk_list,f,protocol=pickle.HIGHEST_PROTOCOL)

def output_json (filename) :
    """
      Export data on Runge-Kutta methods into json file
    """
    if filename == None:
      filename = "rk.json"

    for i,rk in enumerate(runge_kutta_list()) :
      print(rk.label)
      # 1. calculer les données sur rk
      rk_dic = {
        'id': i ,
        'label': rk.label ,
        'order': rk.order() ,
        'stages': rk.stages() ,
        'butcher_tableau': rk.butcher_tableau() ,
        'stability_function': sp.latex(rk.stability_function()) ,
        'scheme': utils.latex_aligned(rk.scheme(shu_osher=True)) ,
        'scheme_lawson': utils.latex_aligned(rk.lawson_scheme(shu_osher=True)) ,
        'stability_domain': [[0,0]] ,
        'order_star': "data:image/png;base64," + "foo", #rk.order_star(base64=True) ,
        'cfl': {
          'weno5': sp.latex(5) ,
          'weno3': sp.latex(3) ,
          'y_max': sp.latex(0)
        }
      }
      print(rk_dic)

      # 2. utiliser jinja pour convertir ceci en json (convertion individuelle)
    # 3. tout écrire dans un fichier json

    print("json "+filename)

def output_cpp (filename) :
    """
      Generate C++ functions to test every Runge-Kutta methods
    """
    if filename == None:
      filename = "rk.h"

    for rk in runge_kutta_list() :
      pass
      # 1. calculer le schéma
      # 2. ne conserver qu'un label (nom de fonction, donc supprimer les espaces, parenthèses, etc. et remplacer par des `-` ou `_`) tableau des étapes (format c++) et opération finale (format c++)

    print("c++ "+filename)

if __name__ == '__main__':
  parser = argparse.ArgumentParser(description='Ponio scripts for code generation.')
  parser.add_argument('--pickles',
          help="Export Runge-Kutta methods list as pickles without ponio classes",
          action='store_const',const=output_pickle,dest='action')
  parser.add_argument('--json',
          help="Export Runge-Kutta methods data as json format for Web application",
          action='store_const',const=output_json,dest='action')
  parser.add_argument('--c++',
          help="Generates C++ code for each Runge-Kutta methods as function",
          action='store_const',const=output_cpp,dest='action')

  parser.add_argument('--output','-o',
          help="Output file name",
          action='store',dest='filename',default=None)

  arg = parser.parse_args(sys.argv[1:])
  arg.action(arg.filename)


import numpy as np
import warnings
warnings.simplefilter('ignore', np.RankWarning)
import math
import operator
import itertools
from math import log

def QuadReg( data ):
	lenData = len(data)
	if lenData < 3:
		raise ValueError( 'data must have >= 3 values' )
	return np.polyfit( np.fromiter( (d[0] for d in data), np.float64, lenData), np.fromiter( (d[1] for d in data), np.float64, lenData), 2 )

def QuadRegRSS( data ):
	lenData = len(data)
	if lenData < 3:
		raise ValueError( 'data must have >= 3 values' )
	x = np.fromiter( (d[0] for d in data), np.float64, lenData )
	y = np.fromiter( (d[1] for d in data), np.float64, lenData )
	p = np.poly1d( np.polyfit(x, y, 2) )
	R = np.fromiter( (y[i] - p(v) for i, v in enumerate(x)), np.float64, lenData )
	return np.dot( R, R )

def QuadRegRSE( data ):
	if len(data) <= 3:
		raise ValueError( 'data must have > 3 values' )
	return QuadRegRSS(data) / (len(data) - 3)

def QuadRegFindOutlier( data ):
	lenData = len(data)
	if lenData <= 3:
		raise ValueError( 'data must have >= 3 values' )
	x = np.fromiter( (d[0] for d in data), np.float64, lenData )
	y = np.fromiter( (d[1] for d in data), np.float64, lenData )
	p = np.poly1d( np.polyfit(x, y, 2) )
	R = np.fromiter( (y[i] - p(v) for i, v in enumerate(x)), np.float64, lenData )
	rmse = math.sqrt( np.dot(R,R) / (lenData-1) )	# dot(R,R) = sum of squares
	return max( ((i, abs(r/rmse)) for i, r in enumerate(R)), key=operator.itemgetter(1) )

def QuadRegRemoveOutliersRobust( data, returnDetails=False ):
	'''
		Return a Quadratic Regression ignoring outliers.

		Approach:
			1) Compute the QR with all data points.
			2) Compute the RMSE (Root Mean Square Error), an estimate of the standard deviation of the Residuals.
			3) Scale each point to a Standard Normal Distribution (Mean=0, Std=RMSE).  Mean is zero for best-fit residuals.
			4) Find the max point that exceeds a threshold distance from the mean (-1..1=68.25%, -2..2=95.44%, -3..3=99.74)
			5) If no such point exists, all the points used in the regression are within the threshold.  Return - no outliers.
			6) Otherwise, eliminate the outlier, compute the QR on the remaining points, go to (2)

		Discussion:
			Pros:
				This will return a quadratic best-fit using points that are "reasonably" for the fit.
			Cons:
				Like other outlier techniques, we can be "swamped" by outliers.
				And, the elimination threshold is somewhat arbitrary.  It can both accept outliers (false positives)
				and eliminate valid observations (false negatives) depending on the threshold.

				Also, O(N*k) where N is the sample size and k is the number of outliers.

			Conclusion:
				With RFID reads, we expect a few "wild" values.  The technique should work well for this case.
	'''
	lenData = len(data)
	if lenData < 3:
		raise ValueError( 'data must have >= 3 values' )

	zThreshold = 1.9

	x = np.fromiter( (d[0] for d in data), np.float64, lenData )
	y = np.fromiter( (d[1] for d in data), np.float64, lenData )

	abc = np.polyfit(x, y, 2)
	p = np.poly1d( abc )
	while len(x) > 3:
		R = np.fromiter( (y[i] - p(v) for i, v in enumerate(x)), np.float64, len(x) )	# Residuals from Best Fit.

		# Root mean square error.
		rmse = math.sqrt( np.dot(R,R) / (len(x)-1) )	# dot(R,R) = sum of squares
		iBest, zMax = None, zThreshold					# 2.0 = 95% of normal distribution within mean.
		for i, r in enumerate(R):
			z = abs(r/rmse)		# Scale residual to standard normal.  Mean is always zero for residuals.
			if z > zMax:
				iBest, zMax = i, z
		if iBest is None:
			break

		# Eliminate the maximum outlier found.
		x = np.delete( x, iBest )
		y = np.delete( y, iBest )
		abc = np.polyfit(x, y, 2)
		p = np.poly1d( abc )

	if returnDetails:
		inliers = tuple( zip(x, y) )
		inliersSet = set( inliers )
		outliers = tuple( d for d in data if d not in inliersSet )
		return abc, inliers, outliers

	return abc

inliersTotal, samplesTotal = 0, 0
def ResetStats():
	global inliersTotal, samplesTotal
	inliersTotal, samplesTotal = 0, 0

def QuadRegRemoveOutliersRansac( data, returnDetails=False ):
	global inliersTotal, samplesTotal

	lenData = len(data)
	if lenData < 3:
		raise ValueError( 'data must have >= 3 values' )

	tMin = min( d[0] for d in data )
	tMax = max( d[0] for d in data )

	def modelValid( model, inliers=None ):
		if model[0] >= 0:
			return False	# Parabola cannot open up

		apexX = -model[1] / (2.0 * model[0])
		if np.polyval(model, apexX) > 0.0:
			return False	# db value must be reasonable.

		# Estimated point must be in range of samples.
		return (tMin <= apexX <= tMax) if inliers is None else (min(x[inliers]) <= apexX <= max(x[inliers]))

	np.random.seed( 123456789 )

	# Number of points used to define a proposed model.
	n = max( lenData // 10, 3 )

	t = 3					# Distance from parabolic curve where a point is considered an inlier.

	minD = int(lenData * 0.75)	# Minimum number of points that must be matched to consider the model valid.
	bestErr = np.inf		# Best Sum of abs Residuals.
	bestModel = None		# Cooefs of the parabolic
	bestD = minD			# Best number of points within threshold distance of model.  Initialize to minimum.

	# Convert input to numpy.
	x = np.fromiter( (d[0] for d in data), np.float64, lenData )
	y = np.fromiter( (d[1] for d in data), np.float64, lenData )

	# Bias the sample to consider the strongest reads.
	indexes = sorted( list(range(lenData)), key=lambda i: data[i][1], reverse=True )
	indexes = np.fromiter( (i for i in indexes[:max(int(lenData*0.75), 6)]), int )

	P = 0.99		# Desired probability that we have found an uncontaminated model.
	K = lenData*10	# Minimum number of samples required to find an uncontaminated model.
	samples = 0

	KBad = lenData	# Max number of allowed bad samples (invalid models).
	samplesBad = 0

	while samples < K and samplesBad < KBad:
		np.random.shuffle( indexes )
		maybeInliers = indexes[:n]
		maybeModel = np.polyfit(x[maybeInliers], y[maybeInliers], 2)
		if not modelValid(maybeModel):
			samplesBad += 1
			continue

		# Get all points within range of model.
		alsoInliers = np.abs(np.polyval(maybeModel, x)-y) < t
		curD = sum( alsoInliers )

		if curD >= bestD:

			betterModel = np.polyfit(x[alsoInliers], y[alsoInliers], 2)
			if not modelValid(betterModel, alsoInliers):
				samplesBad += 1
				continue

			if curD > bestD or bestModel is None:
				bestD = curD
				bestErr = np.inf	# Ensure we accept this better fitting model.

				# Use adaptive estimation to update number of samples required to find
				# a sample model with uncontaminated points.
				if curD < lenData:
					w = float(curD) / float(lenData)
					K = 2.0 * log(1.0 - P) / log(1.0 - w**n)	# Multiply by 2 for extra safety.
				else:
					K = samples		# We have a model that includes all points - time to quit.

			thisErr = np.sum(np.abs(np.polyval(betterModel, x[alsoInliers])-y[alsoInliers]))
			if thisErr < bestErr:
				bestModel = betterModel
				bestErr = thisErr

		samples += 1

	samplesTotal += lenData
	inliersTotal += 0 if bestModel is None else bestD

	if returnDetails:
		if bestModel is None:
			return None, None, None
		inliers = np.abs(np.polyval(bestModel, x)-y) < t
		inliers = tuple( zip(x[inliers], y[inliers]) )
		inliersSet = set( inliers )
		outliers = tuple( d for d in data if d not in inliersSet )
		return bestModel, inliers, outliers

	return bestModel

def QuadRegExtreme( data, f=QuadRegRemoveOutliersRansac ):
	a, b, c = f( data )
	if a >= 0.0:
		raise ValueError( 'invalid quadratic: cannot open up' )
	return -b / (2.0 * a)

if __name__ == '__main__':
	data = '''i	Temperature	Yield
1	50	8.3
1	50	3.3
2	50	2.8
3	50	2.9
4	70	2.3
5	70	2.6
6	70	2.1
7	80	2.5
8	80	2.9
9	80	2.4
10	90	3.0
11	90	3.1
12	90	2.8
13	100	3.3
14	100	3.5
15	100	3.0'''
	points = []
	for line in data.split('\n'):
		if not line.startswith('i'):
			fields = line.split()
			points.append( (float(fields[1]), -float(fields[2])) )

	print( points )
	print( QuadReg(points), QuadRegExtreme(points, QuadReg), QuadRegExtreme(points, QuadRegRemoveOutliersRobust) )


import numpy as np

def rmse(M, M_p):
    """
    Computes Root Mean Square Error.

    Input:
    @M - Actual numpy array
    @M_p - Predicted numpy array

    Returns: Root Mean square error - float
    """
    x_len = M.shape[0]
    y_len = M.shape[1]
    error = 0
    N = x_len * y_len
    for x in range(x_len):
        for y in range(y_len):
            error += ((M[x][y] - M_p[x][y]) ** 2) / N
    error = error ** 0.5
    return error

def top_k(k, M, M_p, ignore=True):
    """
    Returns precision of predicted results in top k ratings.

    Input:
    @M - Actual numpy array.
    @M_p - Predicted numpy array.
    @ignore - Ignores already rated values.

    Returns:
    Precision of predictions in top K - float
    """
    x_len = M.shape[0]
    y_len = M.shape[1]
    precision = []
    for i in range(x_len):
        sorted_M = sorted(M[i], reverse=True)[:k]
        for j in range(y_len):
            precision_count = 0
            if ignore:
                if M[i][j] == 0:
                    try:
                        if sorted_M.index(M_p[i][j]) > -1:
                            precision_count += 1
                    except ValueError as ve:
                        pass
            else:
                try:
                    if sorted_M.index(M_p[i][j]) > -1:
                        precision_count += 1
                except ValueError as ve:
                    pass
        precision.append(precision_count)

    average_precision = 0
    p_len = len(precision)
    for p in precision:
        average_precision += p / p_len
    return average_precision

def spearman_correlation(M, M_p):
    """
    Returns Spearman score for the prediction.
    Formula: 1 - [sum(diff(predicted - actual)^2) / n((n^2)-1)]

    Input:
    @M - Actual numpy array.
    @M_p - Predicted numpy array.

    Returns:
    Spearman score - float
    """
    x_len = M.shape[0]
    y_len = M.shape[1]
    N = 0
    sum = 0
    for i in range(x_len):
        for j in range(y_len):
            if M[i][j] != 0:
                N += 1
                sum += (M[i][j] - M_p[i][j]) ** 2
    N = (N*(N**2 - 1))
    sum = 1 - (sum/N)
    return sum


import numpy as np

def nn_search(X=None, x=None, y=None, z=None,
              n=1,
              return_distances=False):
    """
    Vectorized nearest neighbor search accepting either
    an (N,D) matrix (first kwarg) or three (N,) vectors
    (representing x, y, and z coordinates) to identify
    the nearest neighbors to a selection of points. Uses
    Euclidean distance.

    This is naiively vectorized, so will operate quickly
    but will likely blow up for very large N.

    Core algorithm taken from the
    vectorized_nearest_neighbor.py routine in
    astroML (Statistics, Data Mining, and Machine Learning
    in Astronomy; Ch. 2.5, pg 55).

    Parameters
    -----------
    X   :   (N,D) numpy array, optional
         (N,D) numpy array to perform the NN search.
         This must be provided unless at least two of the
         below x,y,z are provided.

    x,y,z : (N,) numpy arrays, optional
        If X is not provided, can provided 3 1D arrays
        which will be used to construct X. Must provide
        at least two of these if used

    n : integer, optional
        Order of nearest neighbor to search for. By default
        this finds the nearest neighbor (n=1), but can
        easily return second-nearest (n=2), and so on.
        Default : 1.

    return_distances : bool, optional
        By default, returns just the indexes corresponding
        to the nearest neighbor for each value. If True,
        also returns the distances to these values.
    """

    if X is None:
        none_count = int(x is None) + int(y is None) + int(z is None)
        if none_count > 1:
            print("If X is not provided, must provide at least 2 1D arrays")
            raise ValueError

        elif none_count == 0:
            l = [x,y,z]
        elif none_count == 1:
            if (x is None):
                l = [y,z]
            elif (y is None):
                l = [x,z]
            else:
                l = [x,y]

        X = np.array(l).T

    # now do the sorting
    XXT = np.dot(X, X.T)
    Xii = XXT.diagonal()
    D   = Xii - 2 * XXT + Xii[:, np.newaxis]

    #
    # generate array that sorts the distances
    # to get indexes of nearest nth neighbor
    # for each point
    #
    nn_sort = np.argsort(D, axis=1)[:,n]

    if return_distances:
        return nn_sort, np.sqrt(D[np.arange(len(nn_sort)), nn_sort])
    else:
        return nn_sort


'''
Computing the ECDF
100xp

In this exercise, you will write a function that takes as input a 1D array of
data and then returns the x and y values of the ECDF. You will use this function
over and over again throughout this course and its sequel. ECDFs are among the most
important plots in statistical analysis. You can write your own function, foo(x,y)
according to the following skeleton:

def foo(a,b):
    """State what function does here"""
    # Computation performed here
    return x, y

The function foo() above takes two arguments a and b and returns two values x and y.
The function header def foo(a,b): contains the function signature foo(a,b), which
consists of the function name, along with its parameters. For more on writing your
own functions, see DataCamp's course Python Data Science Toolbox (Part 1) here!

Instructions
-Define a function with the signature ecdf(data). Within the function definition,
    -Compute the number of data points, n, using the len() function.
    -The x-values are the sorted data. Use the np.sort() function to perform the sorting.
    -The y data of the ECDF go from 1/n to 1 in equally spaced increments. You can construct
        this using np.arange(). Remember, however, that the end value in np.arange() is not
        inclusive. Therefore, np.arange() will need to go from 1 to n+1. Be sure to divide this by n.
    -The function returns the values x and y.
'''
import numpy as np

def ecdf(data):
    """Compute ECDF for a one-dimensional array of measurements."""

    # Number of data points: n
    n = len(data)

    # x-data for the ECDF: x
    x = np.sort(data)

    # y-data for the ECDF: y
    y = np.arange(1, n+1) / n

    return x, y


#!/usr/bin/env python

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from potential_fields import gradient_planner, combined_potential

# plt.rcParams.update({'font.size': 20})
SMALL_SIZE = 12
MEDIUM_SIZE = 16
BIGGER_SIZE = 26

plt.rc('font', size=SMALL_SIZE)          # controls default text sizes
plt.rc('axes', titlesize=BIGGER_SIZE)     # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE)    # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE)    # fontsize of the tick labels
plt.rc('legend', fontsize=MEDIUM_SIZE)    # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE)  # fontsize of the figure title

def gradient_plot(x,y, gx,gy, skip=10):
    # plt.figure(figsize=(12,8))
    plt.figure()
    Q = plt.quiver(x[::skip, ::skip], y[::skip, ::skip], gx[::skip, ::skip], gy[::skip, ::skip],
                   pivot='mid', units='inches')
    qk = plt.quiverkey(Q, 0.9, 0.9, 1, r'$1 \frac{m}{s}$', labelpos='E',
                       coordinates='figure')
#     plt.scatter(x[::skip, ::skip], y[::skip, ::skip], color='r', s=5)

def GradientBasedPlanner (f, start_coords, end_coords, max_its):
    # GradientBasedPlanner : This function plans a path through a 2D
    # environment from a start to a destination based on the gradient of the
    # function f which is passed in as a 2D array. The two arguments
    # start_coords and end_coords denote the coordinates of the start and end
    # positions respectively in the array while max_its indicates an upper
    # bound on the number of iterations that the system can use before giving
    # up.
    # The output, route, is an array with 2 columns and n rows where the rows
    # correspond to the coordinates of the robot as it moves along the route.
    # The first column corresponds to the x coordinate and the second to the y coordinate

    [gy, gx] = np.gradient(-f);

    route = np.vstack( [np.array(start_coords), np.array(start_coords)] )
    for i in range(max_its):
        current_point = route[-1,:];
#         print(sum( abs(current_point-end_coords) ))
        if sum( abs(current_point-end_coords) ) < 5.0:
            print('Reached the goal !');
            break
        ix = int(round( current_point[1] ));
        iy = int(round( current_point[0] ));
        vx = gx[ix, iy]
        vy = gy[ix, iy]
        dt = 1 / np.linalg.norm([vx, vy]);
        next_point = current_point + dt*np.array( [vx, vy] );
        route = np.vstack( [route, next_point] );
    route = route[1:,:]

    return route

# Generate some points
nrows = 400;
ncols = 600;

obstacle = np.zeros((nrows, ncols));
[x, y] = np.meshgrid (np.arange(ncols), np.arange(nrows));

# Generate some obstacle
obstacle [300:, 100:250] = True;
obstacle [150:200, 400:500] = True;
t = ((x - 200)**2 + (y - 50)**2) < 50**2;
obstacle[t] = True;
t = ((x - 400)**2 + (y - 300)**2) < 80**2;
obstacle[t] = True;
plt.imshow(1 - obstacle, 'gray')

# Compute distance transform
from scipy.ndimage.morphology import distance_transform_edt as bwdist

d = bwdist(obstacle==0) # distance to closest obstacle function
d2 = (d/100.) + 1 # rescale and transform distances
d0 = 2 # radius of potential field influence
nu = 800
repulsive = nu*((1./d2 - 1./d0)**2)
repulsive [d2 > d0] = 0

# Display repulsive potential
fig = plt.figure()
ax = fig.gca(projection='3d')
plt.title ('Repulsive Potential')
# Plot the surface.
surf = ax.plot_surface(x, y, repulsive, cmap=cm.coolwarm, linewidth=0, antialiased=False)

# # Compute attractive force
goal = [400, 50];
start = [50, 350];

xi = 1./700

attractive = xi * ( (x - goal[0])**2 + (y - goal[1])**2 );

# Display attractive potential
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(x, y, attractive, cmap=cm.coolwarm, linewidth=0, antialiased=False)
plt.title ('Attractive Potential')

# # Combine terms
f = attractive + repulsive
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(y, x, f, cmap=cm.coolwarm, linewidth=0, antialiased=False)
plt.title ('Total Potential')
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.xlabel('X [cm]')
plt.ylabel('Y [cm]')
ax.set_zlabel('f(x,y)')

# # Display 2D configuration space
# plt.figure()
# plt.imshow(1-obstacle, 'gray')
# plt.plot (start[0], start[1], 'ro', markersize=10, label='start')
# plt.plot (goal[0], goal[1], 'ro', color='green', markersize=10, label='goal')
# # plt.axis ([0, ncols, 0, nrows]);
# plt.xlabel('X [cm]')
# plt.ylabel('Y [cm]')
# plt.legend()
# plt.title ('2D map with obstacles')

# Plan route
route = GradientBasedPlanner(f, start, goal, 700);

# Compute gradients for visualization
[gx, gy] = np.gradient(-f);

# plt.figure(figsize=(12,8))
# plt.imshow(gy, 'gray')
# plt.title('Gx=df/dx - gradient')

# plt.figure(figsize=(12,8))
# plt.imshow(gx, 'gray')
# plt.title('Gy=df/dy - gradient')

# Velocities plot
skip = 10;
xidx = np.arange(0,ncols,skip)
yidx = np.arange(0,nrows,skip)
gradient_plot(x,y, gy,gx, skip=10)
plt.plot(start[0], start[1], 'ro', color='green', markersize=10, label='Start');
plt.plot(goal[0], goal[1], 'ro', color='red', markersize=10, label='Goal');
plt.plot(route[:,0], route[:,1], linewidth=3, label='Path');
plt.xlabel('X')
plt.ylabel('Y')
plt.legend(loc='lower right')
# plt.title('Gradient plot')

plt.draw()
plt.pause(1)
raw_input('Hit Enter to close')
plt.close('all')


# start import modules
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import emcee
# end import modules

savefig=True

# start generate data
np.random.seed(1)      # for repeatability
F_true = 1000          # true flux, say number of photons measured in 1 second
N = 50                 # number of measurements
F = stats.poisson(F_true).rvs(N)
                       # N measurements of the flux
e = np.sqrt(F)         # errors on Poisson counts estimated via square root
# end generate data

# start visualize data
fig, ax = plt.subplots()
ax.errorbar(F, np.arange(N), xerr=e, fmt='ok', ecolor='gray', alpha=0.5)
ax.vlines([F_true], 0, N, linewidth=5, alpha=0.2)
ax.set_xlabel("Flux");ax.set_ylabel("measurement number");
# end visualize data

if savefig:
    fig.savefig('../fig/singlephotoncount_fig_1.png')

# start frequentist
w=1./e**2
print(f"""
F_true = {F_true}
F_est = {(w * F).sum() / w.sum():.0f} +/- { w.sum() ** -0.5:.0f} (based on {N} measurements) """)
# end frequentist

# start bayesian setup
def log_prior(theta):
    if theta>0 and theta<10000:
        return 0 # flat prior
    else:
        return -np.inf

def log_likelihood(theta, F, e):
    return -0.5 * np.sum(np.log(2 * np.pi * e ** 2) \
                             + (F - theta[0]) ** 2 / e ** 2)

def log_posterior(theta, F, e):
    return log_prior(theta) + log_likelihood(theta, F, e)
# end bayesian setup

# start bayesian mcmc
ndim = 1      # number of parameters in the model
nwalkers = 50 # number of MCMC walkers
nwarm = 1000  # "warm-up" period to let chains stabilize
nsteps = 2000 # number of MCMC steps to take
# we'll start at random locations between 0 and 2000
starting_guesses = 2000 * np.random.rand(nwalkers, ndim)
sampler = emcee.EnsembleSampler(nwalkers, ndim, log_posterior, args=[F,e])
sampler.run_mcmc(starting_guesses, nsteps)
# Shape of sampler.chain  = (nwalkers, nsteps, ndim)
# Flatten the sampler chain and discard warm-in points:
samples = sampler.chain[:, nwarm:, :].reshape((-1, ndim))
# end bayesian mcmc

# start visualize bayesian
fig, ax = plt.subplots()
ax.hist(samples, bins=50, histtype="stepfilled", alpha=0.3, density=True)
ax.set_xlabel(r'$F_\mathrm{est}$')
ax.set_ylabel(r'$p(F_\mathrm{est}|D,I)$');
# end visualize bayesian

if savefig:
    fig.savefig('../fig/singlephotoncount_fig_2.png')

# plot a best-fit Gaussian
F_est = np.linspace(975, 1025)
pdf = stats.norm(np.mean(samples), np.std(samples)).pdf(F_est)
ax.plot(F_est, pdf, '-k')

# start bayesian CI
sampper=np.percentile(samples, [2.5, 16.5, 50, 83.5, 97.5],axis=0).flatten()
print(f"""
F_true = {F_true}
Based on {N} measurements the posterior point estimates are:
...F_est = { np.mean(samples):.0f} +/- { np.std(samples):.0f}
or using credibility intervals:
...F_est = {sampper[2]:.0f}          (posterior median)
...F_est in [{sampper[1]:.0f}, {sampper[3]:.0f}] (67% credibility interval)
...F_est in [{sampper[0]:.0f}, {sampper[4]:.0f}] (95% credibility interval) """)
# end bayesian CI

if not savefig:
    plt.show()


# -*- coding: utf-8 -*-
"""
@author: Maxime

Spectrum of the Hermitian random matrix B+B^T.
"""

import numpy as np
import matplotlib.pyplot as plt

# %%

# The choice of the variables is similar to the article.
# We create the matrix B and display the spectrum of B+B^T.

def plot_capitaine_spectrum(n=100, mu=0, alpha=1, title=True):
    """
    Plot the spectrum of the associated matrix.

    Parameters
    ----------
    N : int
        Dimension of the matrix. The default is 100.
    mu : float
        parameter corresponding to mu. The default is 0.
    alpha : float
        parameter corresponding to alpha. The default is 1.
    title : bool
        presence of the title on the plot. The default is True.

    Returns
    -------
    fig: figure of the spectrum.

    """

    const = (mu*alpha/np.sqrt(n))
    B = (np.random.randn(n, n)+const)*(1/(np.sqrt(n)*alpha))

    eig_B = np.linalg.eigvals(B+B.T)

    # The rest of the function is dedicated to the plot.

    t = np.linspace(-2*np.sqrt(2/alpha**2), 2*np.sqrt(2/alpha**2), 100)

    fig = plt.figure(1, figsize=(10, 6))

    if mu > 1/(np.sqrt(2)*alpha):
        plt.vlines(2*mu+1/(mu*alpha**2), 0, 0.35,
                   linestyles='--', color='k', linewidth=2)
    plt.plot(t, np.sqrt(4*(2/(alpha**2))-t**2) /
             (2*np.pi*(2/alpha**2)), color='k', linewidth=3)
    plt.hist(eig_B.real, density=True, bins=40,
             edgecolor='black', color='#0000003d')
    plt.grid(color='lightgray', linestyle='--')

    plt.xlim(-2, 3.5)
    plt.ylim(0, 0.35)

    plt.xlabel(r"Spectrum", fontsize=15)
    plt.ylabel("Density", fontsize=15)
    if title:
        plt.title(r'Semi-circular law, $\alpha$ = {}, $\mu$ = {} (N = {})'.format(
            alpha, mu, n), fontsize=15)
    plt.show()

    plt.close()

    return fig
# plt.savefig('Spectre_Ginibre_Presentation_Lille.pdf')

# An exemple of the first scenario:
plot_capitaine_spectrum(n=1000, mu=0, alpha=np.sqrt(2), title=False)

# An exemple of the second scenario:
plot_capitaine_spectrum(n=1000, mu=1.5, alpha=np.sqrt(2), title=False)


import numba
import numpy as np
import argparse
import time

run_parallel = numba.config.NUMBA_NUM_THREADS > 1

@numba.njit(parallel=run_parallel)
def logistic_regression(Y,X,w,iterations):
    for i in range(iterations):
        w -= np.dot(((1.0 / (1.0 + np.exp(-Y * np.dot(X,w))) - 1.0) * Y),X)
    return w

def main():
    parser = argparse.ArgumentParser(description='Logistic Regression.')
    parser.add_argument('--dimension', dest='dimension', type=int, default=10)
    parser.add_argument('--points', dest='points', type=int, default=20000000)
    parser.add_argument('--iterations', dest='iterations', type=int, default=30)
    args = parser.parse_args()

    np.random.seed(0)
    D = 3
    N = 4
    iterations = 10
    points = np.ones((N, D))
    labels = np.ones(N)
    w = 2.0*np.ones(D)-1.3
    t1 = time.time()
    w = logistic_regression(labels, points, w, iterations)
    compiletime = time.time()-t1
    print("SELFPRIMED ", compiletime)
    print("checksum ", w)

    D = args.dimension
    N = args.points
    iterations = args.iterations
    print("D=",D," N=",N," iterations=",iterations)
    points = np.random.random((N, D))
    labels = np.random.random(N)
    w = 2.0*np.ones(D)-1.3
    t2 = time.time()
    w = logistic_regression(labels, points, w, iterations)
    selftimed = time.time()-t2
    print("SELFTIMED ", selftimed)
    print("checksum: ", np.sum(w))

if __name__ == '__main__':
    main()


import numpy as np

from model import Model

class Perceptron(Model):

    plot_type = "binary_ones"

    def __init__(self, w=None, b=None, d=2):
        self.w = np.asarray(w) if w is not None \
                               else np.zeros(d)
        self.b = b if b is not None else 0

    def h(self, x):
        # FIXME: np.sign output is in {-1, 0, +1}
        return np.sign(np.dot(self.w.T, x) + self.b)

    def predict(self, X):
        # FIXME: np.sign output is in {-1, 0, +1}
        return np.sign(np.dot(X, self.w) + self.b)

    def plot_predict(self, X):
        return np.dot(X, self.w) + self.b

    def pla(self, X, y, iterations=10):
        """Perceptron Learning Algorithm."""
        y = y.flatten()
        idx = np.arange(X.shape[0])
        for i in range(iterations):
            np.random.shuffle(idx)
            error = False
            for j in idx:
                x = X[j, :].T
                if self.h(x) != y[j]:
                    error = True
                    self.w += y[j] * x
                    self.b += y[j]
                    break
            if not error: return i
        return iterations

    def pocket(self, X, y, iterations=10):
        max_accuracy = 0.0
        y = y.flatten()
        pocket_w = np.array(self.w)
        pocket_b = self.b
        idx = np.arange(X.shape[0])
        for iteration in range(iterations):
            np.random.shuffle(idx)
            for i in idx:
                x = X[i, :].T
                if self.h(x) != y[i]:
                    self.w += y[i] * x
                    self.b += y[i]
                    accuracy = self.accuracy(X, y)
                    if accuracy > max_accuracy:
                        max_accuracy = accuracy
                        pocket_w = np.array(self.w)
                        pocket_b = self.b
                    break
        self.w = pocket_w
        self.b = pocket_b


import numpy as np
from scipy import optimize
from scipy.stats import multivariate_normal
import numpy.typing as npt

def gaussian1D_fct(height: float, center: float, width: float):
    """"""
    width = float(width)
    return lambda x: height * np.exp(-(((center - x) / width) ** 2) / 2)

def gaussian1D_pdf(center: float, width: float):
    """"""
    rv = multivariate_normal(center, width)
    return rv.pdf

def gaussian2D_pdf(
    mean_x: float,
    mean_y: float,
    cov_xx: float,
    cov_xy: float,
    cov_yx: float,
    cov_yy: float,
):
    mean = np.array([mean_x, mean_y])
    cov = np.array([[cov_xx, cov_xy], [cov_yx, cov_yy]])
    rv = multivariate_normal(mean, cov)
    return rv.pdf

def gaussian2D_fct(
    height: float, center_x: float, center_y: float, width_x: float, width_y: float
):
    """
    Use like this:
    v_x: voltages on x axis
    v_y: votlages on y axis

    gauss = gaussian2D(1, center_gauss_x , center_gauss_y, width_x, width_y)
    Xin, Yin = np.meshgrid(v_x, v_y)

    gauss_data = gauss(Xin, Yin)
    """
    width_x = float(width_x)
    width_y = float(width_y)
    return lambda x, y: height * np.exp(
        -(((center_x - x) / width_x) ** 2 + ((center_y - y) / width_y) ** 2) / 2
    )

def moments2D(data: npt.NDArray[np.float64]):
    """"""
    total = data.sum()
    X, Y = np.indices(data.shape)
    x = (X * data).sum() / total
    y = (Y * data).sum() / total
    col = data[:, int(y)]
    width_x = np.sqrt(abs((np.arange(col.size) - y) ** 2 * col).sum() / col.sum())
    row = data[int(x), :]
    width_y = np.sqrt(abs((np.arange(row.size) - x) ** 2 * row).sum() / row.sum())
    height = data.max()
    return height, x, y, width_x, width_y


import numpy as np
from math import exp, pi, cos, sin

def normal(num=100, dimensions=3, a=1.0, b=0.5, mu=None):
        # generate the means of the distribution
        mu = np.array([0.0 for _ in range(dimensions)]) if mu is None else np.array(mu)

        # generate the desired covariance matrix
        r = np.array([[a if col == row else b for col in range(dimensions)] for row in range(dimensions)])

        # Generate the random samples
        return np.random.multivariate_normal(mu, r, size=num)

def sigmoid(start=-5.0, end=5.0, num=50):
        for i in np.linspace(start, end, num):
            yield 1 / (1 + exp(-i))

def spiral(start=0.0, end=3.14 * 1.5, mul=1.0, num=50):
        x = []
        y = []
        for t in np.linspace(start, end, num):
                r = mul * t
                x.append(r * cos(t))
                y.append(r * sin(t))

        return np.array([x, y])


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

def f_1(x):
    return 1/(1+x**2)
def linspace(a,b,n):
    return np.linspace(a, b, n)
def func_trapezoid(points,i,basefunc=f_1):
    return (points[i+1]-points[i])*(basefunc(points[i])+basefunc(points[i+1]))/2
def func_simpsons(points,i,basefunc=f_1):
    return (points[i+1]-points[i])*(basefunc(points[i])+4*basefunc((points[i]+points[i+1])/2)+basefunc(points[i+1]))/6
def func_gaussian(points,i,num=3,basefunc=f_1):
    w,a,b=gen_ort_poly(points[i],points[i+1],num)
    eig,feat=get_zero_point(a,b)#eig=zerospoints
    A=get_param_a(eig,points[i],points[i+1])
    res=0
    for i in range(len(A)):
        res+=A[i]*basefunc(eig[i])
    return res
def calc_poly(poly,n):
    ans=0
    for i in range(len(poly)):
        ans+=poly[i]*n**(len(poly)-i-1)
    return ans
def poly_intergrate(poly,a,b):
    new_r=[0]*(len(poly)+1)
    for i in range(len(poly)):
        new_r[i]=poly[i]/(len(poly)-i)
    return calc_poly(new_r,b)-calc_poly(new_r,a)
def poly_multiply(a,b):
    res=[0]*(len(a)+len(b)-1)
    for i in range(len(a)):
        for j in range(len(b)):
            res[i+j]+=a[i]*b[j]
    return res
def poly_add(a,b):
    c=list(a)
    d=list(b)
    if(len(c)<len(d)):
        e=list(d)
        d=list(c)
        c=list(e)

    res=list(a)
    for i in range(len(d)):
        res[i+len(c)-len(d)]+=d[i]
    return res

def gen_ort_poly(a,b,num):#by recursion ,num=1 returns 1
    w0=[0]
    w1=[1]
    w2=[1]
    bn = 0
    alpha=[0.]*(num-1)
    beta=[0.]*(num-1)
    for j in range(num-1):
        poly_w_w=poly_multiply(w1,w1)
        poly_w_w2=poly_multiply(w0,w0)
        poly_x_w2=poly_multiply([1,0],poly_w_w)
        an=poly_intergrate(poly_x_w2,a,b)/poly_intergrate(poly_w_w,a,b)
        alpha[j]=an
        if(poly_intergrate(poly_w_w2,a,b)!=0):
            bn=poly_intergrate(poly_w_w,a,b)/poly_intergrate(poly_w_w2,a,b)
            beta[j]=bn
        w2=[0]*(len(w1)+1)
        w2=poly_add(w2,poly_multiply([1,0],w1))
        w_t1=poly_multiply([-an],w1)
        w_t2=poly_multiply([-bn],w0)
        w2=poly_add(w2,w_t1)
        w2=poly_add(w2,w_t2)
        w0=list(w1)
        w1 = list(w2)
    return w2,alpha,beta

def get_zero_point(alpha,beta):
    Martrix=np.zeros((len(alpha),len(alpha)))
    for i in range(len(alpha)):
        Martrix[i][i]=alpha[i]
        if(i!=len(alpha)-1):
            Martrix[i][i+1]=1
            Martrix[i+1][i]=beta[i+1]

    eig,featvec=np.linalg.eig(Martrix)
    return eig,featvec

def Gauss_GenLinar(zp,a,b):
    Mat=np.zeros((len(zp),len(zp)))
    Vec=np.zeros((len(zp),1))
    aa=[1]
    for i in range(len(zp)):
        f=list(aa)
        f_inter=poly_intergrate(f,a,b)
        for zeropoint in range(len(zp)):
            Mat[i][zeropoint]=calc_poly(f,zp[zeropoint])
        Vec[i][0]=f_inter
        aa.append(0)
    return Mat,Vec

def get_param_a(zeropoints,a,b):
    Mat,Vec=Gauss_GenLinar(zeropoints,a,b)
    A=np.linalg.solve(Mat,Vec)
    return A

def count_allseg(points,func,num=0):
    res=0.
    for i in range(len(points)-1):
        if(num!=0):
            res+=func(points,i,num)
        else:
            res+=func(points,i)
    print("res:"+str(res))
    bias=math.fabs(math.pi/2 - res)
    print("bias:"+str(bias))

def solve_q1():
    p=linspace(-1,1,20)
    count_allseg(p,func_trapezoid)

def solve_q2():
    p=linspace(-1,1,10)
    count_allseg(p,func_simpsons)
def solve_q3():
    p=linspace(-1,1,10)
    count_allseg(p,func_gaussian,num=3)
def solve_q4():
    p=linspace(-1,1,6)
    count_allseg(p,func_gaussian,num=4)
def solve_q5():
    p=linspace(-1,1,4)
    count_allseg(p,func_gaussian,num=6)

#solve_q1()
#solve_q2()
#solve_q3()
#solve_q4()
solve_q5()


import numpy as np, scipy.integrate as integrate, matplotlib.pyplot as plt
from scipy.stats import norm
from scipy.optimize import minimize

class Newsvendor:
    def __init__(self, instance):
        self.mean, self.std, self.o, self.u = instance["mean"], instance["std"], instance["o"], instance["u"]

    def crit_frac_solution(self): # critical fractile
        return norm.ppf(self.u/(self.o+self.u), loc=self.mean, scale=self.std)

    def cfolf(self,Q): # complementary first order loss function
        return integrate.quad(lambda x: norm.cdf(x, loc=self.mean, scale=self.std), 0, Q)[0]

    def folf(self,Q): # first order loss function
        return self.cfolf(Q)-self.u*(Q - self.mean)

    def C(self,Q): # C(Q)
        return (self.o+self.u)*self.cfolf(Q)-self.u*(Q - self.mean)

    def optC(self): # min C(Q)
        return minimize(self.C, 0, method='Nelder-Mead')

instance = {"o" : 1, "u": 5, "mean" : 10, "std" : 2}
nb = Newsvendor(instance)
print("Q^*=" + str(nb.crit_frac_solution()))
print("C(Q^*)=" + str(nb.C(nb.crit_frac_solution())))
print(nb.optC())

plt.plot([nb.C(Q) for Q in np.arange(7,20,0.1)], label="C(Q)")
plt.plot([nb.folf(Q) for Q in np.arange(7,11,0.1)], label="uE[max(d-Q,0)]")
plt.plot([nb.cfolf(Q) for Q in np.arange(7,20,0.1)], label="oE[max(Q-d,0)]")
plt.legend()
plt.show()


import numpy as np
import sys
from six import StringIO, b

from gym import utils
from gym.envs.toy_text import discrete

from scipy.stats import poisson
from .jcr_mdp import *

MAX_CARS = 20 # maximum # of cars in each location
MAX_MOVE_OF_CARS = 5 # maximum # of cars to move during night

Ptrans = create_P_matrix()

R = create_R_matrix()

class JacksCarRentalEnv(discrete.DiscreteEnv):
    """
    Jack’s Car Rental

    Jack manages two locations for a nationwide car rental company.
    Each day, some number of customers arrive at each location to rent cars.
    If Jack has a car available, he rents it out and is credited $10 by
    the national company. If he is out of cars at that location, then the
    business is lost. Cars become available for renting the day after they
    are returned. To help ensure that cars are available where they are
    needed, Jack can move them between the two locations overnight, at a cost
    of $2 per car moved. We assume that the number of cars requested and
    returned at each location are Poisson random variables. Suppose Lambda is
    3 and 4 for rental requests at the first and second locations and
    3 and 2 for returns.

    To simplify the problem slightly, we assume that there can be no more than
    20 cars at each location (any additional cars are returned to the
    nationwide company, and thus disappear from the problem) and a maximum
    of five cars can be moved from one location to the other in one night.

    We take the discount rate to be gamma = 0.9 and formulate this as a
    continuing finite MDP, where the time steps are days, the state is the
    number of cars at each location at the end of the day, and the actions
    are the net numbers of cars moved between the two locations overnight.

    """

    metadata = {'render.modes': ['human', 'ansi']}

    def __init__(self):
        print("")

        # n.b. not all actions are availabe in all states
        # i.e. we cannot  move 5 cars if there are less than 5 cars at location
        actions = np.arange(-MAX_MOVE_OF_CARS, MAX_MOVE_OF_CARS + 1)
        nA = len(actions)

        # can be MAX_CARS at each location A, B
        nS = (MAX_CARS + 1)**2

        # obtain one-step dynamics for dynamic programming setting
        P = {
            s: {
                a: [
                    (Ptrans[next, s, a], next, R[s, a], False)
                    for next in range(nS)
                ]
                for a in range(nA)
            }
            for s in range(nS)
        }

        # isd initial state dist
        isd = np.full(nS, 1 / nS)

        # This allows easier plotting of policy and value functions:
        # e.g. V.reshape(env.observation_shape) will give a 21x21 matrix
        observation_shape = (MAX_CARS + 1, MAX_CARS + 1)

        super(JacksCarRentalEnv, self).__init__(nS, nA, P, isd)

        # The following three elements enable this enviroment to be interfaced with
        # the dynamic programming algorithms of the doctrina library:
        # https://github.com/rhalbersma/doctrina/blob/master/src/doctrina/algorithms/dp.py
        # https://github.com/rhalbersma/doctrina/blob/master/exercises/dp-gym_jcr.ipynb

        # Equation (3.4) in Sutton & Barto (p.49):
        # p(s'|s, a) = probability of transition to state s', from state s taking action a.
        self.transition = Ptrans.transpose(1, 2, 0)
        assert np.isclose(self.transition.sum(axis=2), 1).all()

        # Equation (3.5) in Sutton & Barto (p.49):
        # r(s, a) = expected immediate reward from state s after action a.
        self.reward = R


# -*- coding: utf-8 -*-
"""
Created on Sat Mar 31 09:02:23 2018

@author: David A. Miranda, PhD
"""

import numpy as np
import matplotlib.pyplot as plt
from sympy import *
init_session()

x,p,hbar = symbols('x,p,hbar')
n = symbols('n', integer=True)
psi = cos(n*pi*x/4)/sqrt(2)

pretty_print(('psi(x)',psi))

E_x2 = simplify(integrate(x**2 * psi**2, (x, -2, 2)))
E_x  = simplify(integrate(x * psi**2, (x, -2, 2)))
sigma2_x = (E_x2 - E_x**2).simplify()

pretty_print(('sigma_x^2', sigma2_x))

E_p2 = simplify(-hbar**2*integrate(psi * diff(psi, x, 2), (x, -2, 2)))
E_p  = simplify(-1j*hbar*integrate(psi * diff(psi, x), (x, -2, 2)))

sigma2_p = (E_p2 - E_p**2).simplify()

pretty_print(('sigma_p^2', sigma2_p))

s2_x = sigma2_x.args[1][0].evalf()
s2_p = sigma2_p.args[1][0].evalf()

s2_p = s2_p.subs({hbar:1})  # It doew hbar = 1

sx = np.array([])
sp = np.array([])

n_eval = np.arange(1,10)

for nn in n_eval:
    sx = np.append(sx, sqrt(s2_x.subs({n:nn})))
    sp = np.append(sp, sqrt(s2_p.subs({n:nn})))

plt.plot(n_eval, sx*sp, 'ko', label=r'$\sigma_x \sigma_p/\hbar$')
plt.plot(n_eval, sp, 'k^', label=r'$\sigma_p/\hbar$')
plt.plot(n_eval, sx, 'kv', label=r'$\sigma_x$')

plt.xlabel('n')
plt.legend()


# kf_resistor_demo.py
# Python script to demonstrate a simple, static, 1-state KF model using both measurement and process noise,
# and Kalman gain.  Full-up 1-state Kalman filter, but trivial dynamics (constant dynamics = static).

# Demo Suggestions:  Try running with 1) Initial estimates too low  2) Initial estimate too high
# 3) large sigmas  4)  small sigmas  5) large sample size  6) small sample size
import numpy as np
import matplotlib.pyplot as plt
import movavgs as ma
import math

num_samples = 50
truth = 220.6  # static state model
truth_vec = truth*np.ones((num_samples,))

# Kalman-specific parameters:
Phi = 1      # Constant dynamics
Q   = 0.5**2    # Process noise covariance (1x1 matrix):  Should be small since we "know" process is constant!
P0  = 1.0**2    # Initial estimation error covariance (1x1 matrix)
R   = 0.5**2 # Initial measurement noise covariance (1x1 matrix)
# Measurement:
rg = np.random.default_rng(1)
mu=0
sigma = math.sqrt(R)
z = truth_vec + rg.normal(mu,sigma,num_samples)

H=1        # Measurement matrix (1x1)

x_true = truth_vec  # True state
x_hat = np.zeros((num_samples,))    # Data series to hold state estimates
# initial percentage error in guess:
init_percent = 0.01
x_hat[0]= z[0] # Initial state estimate / guess; could also be based on truth + init_percent*truth
P = np.zeros((num_samples,))        # Estimation error covariance matrix
P[0]=P0                             # Initial covariance
pre = np.zeros((num_samples,))      # Prior covariance vector for plotting
post = np.zeros((num_samples,))     # Posterior covariance for plotting
post[0] = P[0]                      # Initial pre value
pre[0] = P[0]                       # Initial post value

# Main Kalman loop:

for k in range(1,num_samples):   # Start at k=2, because we already have k=1 as initial values
    prior_est = x_hat[k-1]      # Static problem no dynamics!
    prior_P = P[k-1] + Q        # Temporary prior P:  Simply add process noise, since there are no dynamics
    pre[k] = prior_P            # Set pre covariance for this k
    K = prior_P*H/(H*prior_P*H + R)  # Compute Kalman gain; note: no inverses or transposes needed
    x_hat[k] = prior_est + K*(z[k]-H*prior_est)  # State update using prior estimate, measurement and Kalman gain
    P[k] = (1-K*H)*prior_P*np.transpose(1-K*H) + K*R*np.transpose(K)     # Covariance update
    post[k] = P[k]              # Set posterior vector for plotting

# Plot results:
time = np.arange(0,num_samples)
#plt.figure(1)
#plt.plot(time,x_true,'b--*',time,z,'r--o',time,x_hat,'d:k')
#plt.legend(['Truth','Measurements','Estimates'],loc='lower right')
#plt.xlabel('Time')
#plt.ylabel('Resistance (Ohms)')
#plt.title('Kalman Estimator: Resistor')
#plt.ylim([truth-init_percent*truth*10,truth+init_percent*truth*10])
#plt.show()

print('Estimate: ', x_hat)
print('Residual: ', x_hat-x_true)

avgtype = 'Kalman Filter'
title = avgtype + ' Estimator:  ' + ' Resistance '
ylabel = 'Resistance (Ohms)'
ma.tme_plot(2,truth_vec, z, x_hat, ylabel, title, sigma)

# To get a sawtooth plot, you can use the attached function I wrote called collate.m.
# If you have two vectors of variances, v1, v2, and one is supposed to be before measurement update and one after, you create
# a new vector twice in length by v=collate(v1,v2).   Then, if t is a the time vector for the period in question, you collate it with itself by
#new_time = collate(t,t).   Then you can plot v against new_time:   plot(new_time, v).

def collate(v1,v2):
# collate.m collates two vectors of length m into a single vector of
# length 2m.
    if v1.shape==v2.shape:
        (m,)=v1.shape
        collated = np.empty((2*m,),dtype='object')
#        collated = collated.astype(type(v1[0]))
        j=0
        for i in range(m):
            collated[j]=v1[i]
            collated[j+1]=v2[i]
            j=j+2
    else:
        collated=-1
    return collated

collated = collate(pre,post)
t=time
new_time = collate(t,t)
plt.figure(3)
plt.plot(new_time,collated)
plt.title('Variance in Resistance (some Process Noise)')
plt.xlabel('Time')
plt.ylabel('Variance (Ohms^2)')
plt.show()


import numpy as np

def gauss2D(shape=(3, 3),sigma=0.5):
    """
    2D gaussian mask - should give the same result as MATLAB's
    fspecial('gaussian',[shape],[sigma])
    """
    m, n = [(ss-1.)/2. for ss in shape]
    y, x = np.ogrid[-m:m+1,-n:n+1]
    h = np.exp(-(x*x + y*y) / (2.*sigma*sigma))
    h[ h < np.finfo(h.dtype).eps*h.max() ] = 0
    sumh = h.sum()
    if sumh != 0:
        h /= sumh
    return h


import math
import numpy as np
import pandas as pd
import theano.tensor as T

def log_stability(x):
    """Log-stability for computing loss

    :param x: Input value
    :type x: float
    :return: Scaled value where :math:`\\hat{x} \\in (0, 1)`
    :rtype: float
    """
    if x == 0:
        return 10e-9
    elif x == 1:
        return 1.0-10e-9
    else:
        return x

def binary_cross_entropy(y_true, y_pred, tensor=False):
    """Binary cross-entropy

    :param y_true: Real values on which to compare.
    :type y_true: numpy.array
    :param y_pred: Predicted values.
    :type y_pred: numpy.array
    :param tensor: Perform computation as tensor (theano type), defaults to False
    :type tensor: bool, optional
    :formula: :math:`loss = y_{i} \\log \\left[ \\hat{y}_{i} \\right] + (1 - y_{i}) \\log \\left[1 - \\hat{y}_{i} \\right]`
    :references: * Friedman, J., Hastie, T. and Tibshirani, R., 2001. `The elements of statistical learning <https://web.stanford.edu/~hastie/Papers/ESLII.pdf>`_. Ch. 2, pp. 24.
    :return: Binary cross-entropy
    :rtype: float
    """
    if tensor:
        loss = -y_true * T.log(y_pred) - (1-y_true) * T.log(1-y_pred)
    else:
        loss = (-y_true * np.log(y_pred) - (1-y_true) * np.log(1-y_pred)).sum()
    # print(loss)
    return loss

def mean_squared_error(y_true, y_pred, root=False):
    """Mean Squared Error

    :param y_true: Real values on which to compare.
    :type y_true: numpy.array
    :param y_pred: Predicted values.
    :type y_pred: numpy.array
    :param root: Return Root Mean Squared Error (RMSE), defaults to False.
    :type root: bool, optional

    :formula: :math:`loss = \\dfrac{1}{m} \\times \\sum_{i=1}^{m} (y_i - \\hat{y}_i)^2`

    :references: * Friedman, J., Hastie, T. and Tibshirani, R., 2001. `The elements of statistical learning <https://web.stanford.edu/~hastie/Papers/ESLII.pdf>`_. Ch. 2, pp. 24.

    :return: Mean Squared Error or its root.
    :rtype: float
    """

    loss = (y_pred - y_true)**2
    if root:
        return math.sqrt(loss.mean())
    else:
        return loss.mean()

def binary_accuracy(y_true, y_pred):
    """Accuracy for binary data

    :param y_true: Real values on which to compare.
    :type y_true: numpy.array
    :param y_pred: Predicted values
    :type y_pred: numpy.array

    :return: Binary accuracy (in percent)
    :rtype: float
    """
    true = y_true if type(y_true) == list else [x[0] for x in np.asarray(y_true)]
    pred = y_pred if type(y_pred) == list else [x[0] for x in np.asarray(y_pred)]

    perf = pd.DataFrame({'true': true, 'pred': pred})
    return (perf.true == perf.pred).mean()


# -*- Mode: Python3; coding: utf-8; indent-tabs-mpythoode: nil; tab-width: 4 -*-

'''
    Experiência de análise de imagem de folhas, usando o IFV.

    Índice de Folha Verde (Green Leaf Index)
    ========================================

          2 × G − R − B
    IFV = --------------
          2 × G + R + B

        # Valores positivos indicam as áreas com tonalidade verde.

    Referência:
    LOUHAICHI,M.; BORMAN,M.M.; JOHNSON,D.E. Spatially located platform
    and aerial photography for documentation of grazing impacts on wheat.
    Geocarto International, Taylor & Francis, v. 16, n. 1, p. 65–70, 2001.
'''

import os
import numpy as np
from PIL import Image

PATH = "../Images/"

def calc(data):

    # Índice de folha verde
    # ifv = lambda R,G,B: (2 * G - R - B) / (2 * G + R + B)
    def ifv(R, G, B):
        return (2 * G - R - B) / (2 * G + R + B)

    # Formatar
    h, w, c = data.shape
    v = data.flatten()

    # Calcular
    ifv_list = [ifv(v[i], v[i + 1], v[i + 2]) for i in range(0, h * w * c, c)]
    points = [int(255 * i) if i > 0 else 0 for i in ifv_list]

    # Reformatar
    npArray = np.array(points).reshape(h, w)  # Tons de Cinza
    npArray = np.uint8(npArray)

    return npArray

def view(data):
    img = Image.fromarray(data)
    img.show()

def saveImg(filename, data):
    filename = PATH + filename
    img = Image.fromarray(data)
    img.save(filename)
    print("save ", filename)

def test(filename):
    img_np = PATH + filename + ".npy"
    print("Data: ", img_np)

    if not os.path.exists(img_np):
        print("File not found!")
        return

    data = np.load(img_np)

    h, w, c = data.shape
    if c > 3:
        data = data[:, :, :3]

    result = calc(data)
    view(result)
    saveImg(filename + "_ifv.png", result)

if __name__ == '__main__':

    # ndArray
    px1 = [0, 255, 0]
    px2 = [255, 255, 255]
    pixels = np.array(px1 * 15 + px2 * 5)
    pixels = np.array(pixels).reshape(5, 4, 3)
    result = calc(pixels)
    print(result)

    # ndArray (Imagem)
    test("folha_cafeeiro_doente")
    test("cheflera_variegata")


import numpy as np

def atan2f(y, x, tolerance=1e-6, to_degree=False):
    zero_y = np.abs(y) <= tolerance
    zero_x = np.abs(x) <= tolerance
    if zero_x and zero_y:
        rad = 0
    elif zero_x and (not zero_y):
        rad = np.pi/2 if y > tolerance else -np.pi/2
    elif (not zero_x) and zero_y:
        rad = 0 if x > tolerance else np.pi
    else:
        rad = np.arctan2(y, x)
    if to_degree:
        return np.rad2deg(rad)
    else:
        return rad

def angle_diff(comp_src, comp_dst, offset=0, tolerance=1e-6, wrap=True, to_degree=False):
    zero_src = np.abs(comp_src) <= tolerance
    zero_dst = np.abs(comp_dst) <= tolerance
    if zero_src and zero_dst:
        rad = 0
    elif zero_src and (not zero_dst):
        rad = np.angle(comp_dst)
    elif (not zero_src) and zero_dst:
        rad = -np.angle(comp_src)
    else:
        rad = np.angle(comp_dst) - np.angle(comp_src)
    rad += offset
    if wrap:
        rad = np.mod(rad, 2 * np.pi)
    if to_degree:
        return np.rad2deg(rad)
    else:
        return rad


import numpy as np
from sklearn.cluster import KMeans
from tqdm import tqdm
from pprint import pprint
import matplotlib.pyplot as plt
np.random.seed(0)

# a function to calculate the gammas/responsibilities
def get_gammas(x, m_k, s_k, p_k):
    N = x.shape[0]
    K = p_k.shape[0]

    # at every step of the algorithm gamma is recomputed through the new values of p_k, m_k, s_k
    # at the first step we compute it with the randomly initialized p_k, m_k, s_k
    gammas = np.zeros((N, K))
    for k in range(K):
        denominator = 1 / np.sqrt(2 * np.pi * s_k[k])
        numerator = np.exp(-np.power((x - m_k[k]), 2) / (2 * s_k[k]))
        gammas[:, k] = p_k[k] * np.prod(denominator * numerator, axis=1)

    denominator_gamma = np.sum(gammas, axis=1)
    # 2nd
    for k in range(K):
        gammas[:, k] = gammas[:, k] / denominator_gamma

    return gammas, denominator_gamma

# a function to calculate the reconstruction error of the image
def get_reconstruction_error(og_image, reconstructed_img):
    error = np.sum((np.linalg.norm(og_image - reconstructed_img, axis=1) ** 2)) / og_image.shape[0]
    return error

# a function to calculate the maximum log-likelihood---p_x is the denominator_gamma from the get_gammas function
def get_log_likelihood(p_x):
    tmp = np.log(p_x)
    return np.sum(tmp)

# a function that calculates the log-likelihood using the logsumexp trick
def get_log_likelihood_trick(f_s, m):
    tmp = np.log(np.sum(np.exp(f_s - m[:, np.newaxis]), axis=1))
    return np.sum(m + tmp, axis=0)

# create f_s to use them for the logsumexp trick
def calculate_f_s(X, p_k, m_k, s_k):
    K = s_k.shape[0]

    # for every pixel we calculate k -> f_S
    f_s = np.zeros((X.shape[0], K))
    for k in range(K):
        m = m_k[k, :]
        tmp = np.sum(((X - m)**2 / s_k[k]) + np.log(2*np.pi*s_k[k]), axis=1)
        f_s[:, k] = np.log(p_k[k]) - (tmp / 2)

    m = f_s.max(axis=1)
    return f_s, m

# a function to train the EM algorithm
def train(X, k, iterations):
    N = X.shape[0]
    D = X.shape[1]

    # initialize parameters to random numbers
    m_k, s_k, p_k = initialize_parameters(k, D)
    tolerance = 1e-6

    # initialize the first (random) loss to -Inf so the first (calculated) loss will be larger.
    loss_old = -np.Inf
    gammas = None

    # Algorithm: (1) E-step  (2) M-step  (3) Calculate Loss
    for _ in tqdm(range(iterations)):

        # E-step
        gammas, _ = get_gammas(X, m_k, s_k, p_k)

        # M-step
        # update the parameters / at the first iteration we update with the random gammas (from the random m_k, s_k, p_k)
        m_k, s_k, p_k = update_parameters(gammas, X)

        # # calculate the new loss with the updated parameters
        f, m = calculate_f_s(X, p_k, m_k, s_k)
        loss_new = get_log_likelihood_trick(f, m)

        if loss_new - loss_old < 0:
            print('There is an error in the implementation!!!')
            print('The losses are: {}/{}'.format(loss_new, loss_old))
            exit(0)

        if np.abs(loss_new - loss_old) < tolerance:
            print('Tolerance reached')
            print('The losses are: {}/{}'.format(loss_new, loss_old))
            print(np.sum(p_k))
            return gammas, m_k

        print('The losses are: {}/{}'.format(loss_new, loss_old))
        loss_old = loss_new

    print('Maximum number of iterations reached!!')
    return gammas, m_k

# a function that executes a step of the EM algorithm
# def EM_step(X, m_k, p_k, s_k):
#
#     # calculate the new responsibilities
#     gammas, p_x = get_gammas(X, p_k, m_k, s_k)
#
#     # update the parameters
#     m_k, p_k, s_k = update_parameters(gammas, X)
#
#     return gammas, p_x, m_k, p_k, s_k

# a function to initialize all the values that we will need
def initialize_parameters(k, d):

    # initialize the means m_k
    m_k = np.random.uniform(0.1, 0.9, (k, d))

    # initialize the variance
    s_k = np.random.uniform(0.1, 0.9, k)

    # initialize the mixing coefficients p_k
    # fill the array with numbers = 1/clusters, so they sum up to 1.
    p_k = np.full(k, 1/k)

    return m_k, s_k, p_k

# a function to update the parameters
def update_parameters(gammas, X):
    N = X.shape[0]
    D = X.shape[1]
    K = gammas.shape[1]

    m_k_new = np.zeros((K, D))
    p_k_new = np.zeros(K)
    s_k_new = np.zeros(K)

    # sum up all the gammas for each x_n
    N_k = np.sum(gammas, axis=0)

    # for each K
    for k in range(K):
        # for the new m_k (means)
        gamma_k = gammas[:, k]
        # print(gamma_k.shape)
        for d in range(D):
            m_k_new[k, d] = np.sum(gamma_k * X[:, d], axis=0) / N_k[k]

        # for the new s_k (variance)
        tmp = np.power((X - m_k_new[k, :]), 2)
        tmp = np.sum(tmp, axis=1)
        s_k_new[k] = np.sum(tmp * gamma_k, axis=0) / (N_k[k] * D)

        # for the new p_k (mixing coefficients)
        p_k_new[k] = N_k[k] / N

    return m_k_new, s_k_new, p_k_new

# a function to run the experiments for various K
def experiments(k, X):
    # train for a certain k
    gammas, m_k = train(X, k, iterations=100)
    #
    reconstructed_img = np.zeros(X.shape)
    # for each pixel
    for i in range(X.shape[0]):
        # find the gamma with the maximum value for this particular pixel
        idx = gammas[i].argmax()
        # get the corresponding mean / where the 3 values of the mean represent the RGB values.
        reconstructed_img[i] = m_k[idx]

    og_image = X
    error = get_reconstruction_error(og_image, reconstructed_img)
    print('The reconstruction error for {} is: {}'.format(k, error))

    # reshape the image to its original shape
    reconstructed_img = reconstructed_img.reshape(og_height, og_width, dimensions)
    plt.imshow(reconstructed_img)
    plt.savefig("reconstructed_img_{}.jpg".format(k))

# image = plt.imread('drive/My Drive/MachineLearning/GMM_EM/Image_Segmentation_Compression_GMM/im.jpg')
image = plt.imread('im.jpg')
print('Image dimensions: ({},{})'.format(image.shape[0], image.shape[1]))
og_height = image.shape[0]
og_width = image.shape[1]
dimensions = image.shape[2]
pixels = og_height * og_width

data = image.reshape(pixels, dimensions)
# normalize the data
data = data / 255
experiments(k=2, X=data)


import math as mt
import matplotlib.pyplot as plt
from sympy import *
def f(x):
    return x*mt.sin(x)
x0=1
h=0.0001
fow=(f(x0+h)-f(x0))/h
back=(f(x0)-f(x0-h))/h
cent=(f(x0+h)-f(x0-h))/2*h
print(fow)
plt.plot(x0,f(x0))
##### Untuk Mengatahui Persamaan Hasil Turunan#########
x=Symbol("x")
fd=x*sin(x)
dydx=fd.diff(x)
print("turunan pertama =",dydx)
####### Mengetahui Hasil Sebenarnya dari Perhitungan Turunan Kontinu ########
fx=lambdify(x,dydx)
print("f'(1) =",fx(1))
er=abs(fx(1)-fow)
print("Nilai Eror dari hasil perhitungan =", er)


# Simulate a Thomas cluster process on a rectangle.
# Author: H. Paul Keeler, 2018.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts
# For more details, see the post:
# hpaulkeeler.com/simulating-a-thomas-cluster-point-process/

import numpy as np;  # NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt  # For plotting

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

# Simulation window parameters
xMin = -.5;
xMax = .5;
yMin = -.5;
yMax = .5;

# Parameters for the parent and daughter point processes
lambdaParent = 10;  # density of parent Poisson point process
lambdaDaughter = 100;  # mean number of points in each cluster
sigma = 0.05;  # sigma for normal variables (ie random locations) of daughters

# Extended simulation windows parameters
rExt=6*sigma; # extension parameter
# for rExt, use factor of deviation sigma eg 5 or 6
xMinExt = xMin - rExt;
xMaxExt = xMax + rExt;
yMinExt = yMin - rExt;
yMaxExt = yMax + rExt;
# rectangle dimensions
xDeltaExt = xMaxExt - xMinExt;
yDeltaExt = yMaxExt - yMinExt;
areaTotalExt = xDeltaExt * yDeltaExt;  # area of extended rectangle

# Simulate Poisson point process for the parents
numbPointsParent = np.random.poisson(areaTotalExt * lambdaParent);# Poisson number of points
# x and y coordinates of Poisson points for the parent
xxParent = xMinExt + xDeltaExt * np.random.uniform(0, 1, numbPointsParent);
yyParent = yMinExt + yDeltaExt * np.random.uniform(0, 1, numbPointsParent);

# Simulate Poisson point process for the daughters (ie final poiint process)
numbPointsDaughter = np.random.poisson(lambdaDaughter, numbPointsParent);
numbPoints = sum(numbPointsDaughter);  # total number of points

# Generate the (relative) locations in Cartesian coordinates by
# simulating independent normal variables
xx0 = np.random.normal(0, sigma, numbPoints);  # (relative) x coordinaets
yy0 = np.random.normal(0, sigma, numbPoints);  # (relative) y coordinates

# replicate parent points (ie centres of disks/clusters)
xx = np.repeat(xxParent, numbPointsDaughter);
yy = np.repeat(yyParent, numbPointsDaughter);

# translate points (ie parents points are the centres of cluster disks)
xx = xx + xx0;
yy = yy + yy0;

# thin points if outside the simulation window
booleInside = ((xx >= xMin) & (xx <= xMax) & (yy >= yMin) & (yy <= yMax));
# retain points inside simulation window
xx = xx[booleInside];
yy = yy[booleInside];

# Plotting
plt.scatter(xx, yy, edgecolor='b', facecolor='none', alpha=0.5);
plt.xlabel("x");
plt.ylabel("y");
plt.axis('equal');


import numpy as np

# f(x) = a*x*x*x + b*x*x + c*x + d
# f'(x) = 3*a*x*x + 2*b*x + c
#
# d = x0
# c = dx0
# a + b + c + d = x1
# 3*a + 2*b + c = dx1
#
# a + b + dx0 + x0 = x1
# a + b = x1 - x0 - dx0
# a = x1 - x0 - dx0 - b
#
# 3*a + 2*b + dx0 = dx1
# 3*a + 2*b = dx1 - dx0
# 3*(x1 - x0 - dx0 - b) + 2*b = dx1 - dx0
# -3*b + 2*b = dx1 - dx0 - 3*(x1 - x0 - dx0)
# b = -dx1 + dx0 + 3*(x1 - x0 - dx0)
#
# a = x1 - x0 - dx0 - 0.5 * (dx1 - dx0 - 3*(x1 - x0 - dx0))
def cubic_spline_coeffs(p0, v0, p1, v1):
    d = p0
    c = v0
    b = -v1 + v0 + 3*(p1 - p0 - v0)
    a = p1 - p0 - v0 - b
    return [a,b,c,d]

def cubic_spline_coeffs_list(ps, vs):
    return [ cubic_spline_coeffs(ps[i], vs[i], ps[i+1], vs[i+1]) for i in range(len(ps)-1) ]

def cubic_spline(N, ps=None, vs=None, coeffs_list=None):
    t = np.linspace(0,1,N)[np.newaxis].T

    if coeffs_list is None:
        coeffs_list = cubic_spline_coeffs_list(ps, vs)

    vs = []
    for a,b,c,d in coeffs_list:
        v = a*t**3 + b*t**2 + c*t + d
        vs.append(v)

    return np.concatenate(vs).T

if __name__ == "__main__":
    import matplotlib.pyplot as plt
    Np=4
    Nt=100
    p = np.random.random((Np,2))*2-1
    v = np.random.random((Np,2))*2-1
    xy = cubic_spline(p, v, Nt)
    plt.plot(xy[0,:], xy[1,:])
    plt.show()


"""Routine to solve the Poisson system with Jacobi."""

import numpy

def solve_jacobi(grid, ivar, rvar, options, verbose=False):
    """Solve the Poisson system using a Jacobi method.

    Arguments
    ---------
    grid : Grid object
        Grid containing data.
    ivar : string
        Name of the grid variable of the numerical solution.
    rvar : string
        Name of the grid variable of the right-hand side.
    maxiter : integer, optional
        Maximum number of iterations;
        default: 3000
    tol : float, optional
        Exit-criterion tolerance;
        default: 1e-9

    Returns
    -------
    ites: integer
        Number of iterations computed.
    residual: float
        Final residual.
    verbose : bool, optional
        Set True to display convergence information;
        default: False.

    """

    maxiter = options['maxiter']
    tol = options['tol']

    phi = grid.get_values(ivar)
    b = grid.get_values(rvar)
    dx, dy = grid.dx, grid.dy

    ites = 0
    residual = tol + 1.0
    while ites < maxiter and residual > tol:
        phi_old = numpy.copy(phi)  # previous solution
        phi[1:-1, 1:-1] = (((phi_old[1:-1, :-2] +
                             phi_old[1:-1, 2:]) * dy**2 +
                            (phi_old[:-2, 1:-1] +
                             phi_old[2:, 1:-1]) * dx**2 -
                            b[1:-1, 1:-1] * dx**2 * dy**2) /
                           (2 * (dx**2 + dy**2)))

        grid.fill_guard_cells(ivar)

        residual = (numpy.sqrt(numpy.sum((phi - phi_old)**2) /
                    ((grid.nx + 2) * (grid.ny + 2))))
        ites += 1

    if verbose:
        print('Jacobi method:')
        if ites == maxiter:
            print('Warning: maximum number of iterations reached!')
        print('- Number of iterations: {}'.format(ites))
        print('- Final residual: {}'.format(residual))

    return ites, residual


import numpy as np

### Q1

class convolution():
    def __init__(self, n_filters, kernel_size, padding, stride, activation=None):
        self.output_channel = n_filters
        self.filter_size = kernel_size[0]

        if padding == "VALID":
                self.pad = 0
        else:
            self.pad = (self.filter_size -1)//2

        self.stride = stride
        self.activation = activation

    def forward(self, X): #, figures=None):
        self.input = X
        self.batch_size = X.shape[0]
        self.input_size = X.shape[1]
        self.input_channel = X.shape[3]

        self.filters = np.random.randn(self.output_channel, self.filter_size, self.filter_size, self.input_channel)

        output_dim = int(((self.input.shape[1]-self.filter_size + 2 * self.pad)// self.stride)+1)
        output_image = np.zeros((self.batch_size, output_dim, output_dim, self.output_channel))

        X_padded = np.pad (X, ((0,0), (self.pad, self.pad), (self.pad, self.pad), (0,0)), 'constant', constant_values=0)

        for r in range(self.batch_size):
            for k in range(self.output_channel):
                filter = self.filters[k]
                for i in range(output_dim): #horizontal axis
                    for j in range(output_dim): # vertical axis
                        output_image[r,i,j,k] = np.multiply(filter, X_padded[r,i* self.stride + self.filter_size,
                                                            j * self.stride: j * self.stride + self.filter_size, :]).sum()

### Q2

# outputOfEachConvLayer = [(in_size + 2*padding - kernel_size) / stride] + 1

def X_flatten(self, X, window_h, window_w, window_c, out_h, out_w, stride=1, padding=0):
    X_padded = np.pad (X, ((0,0), (padding, padding), (padding, padding), (0,0)), 'constant', constant_values=0)

    windows = []
    for i in range(out_h):
        for j in range(out_w):
            window = X_padded[:,i *stride:i * stride + window_h, j * stride: j*stride + window_w, :]
            windows.append(window)
    stacked = np.stack(windows)
    return np.reshape(stacked, (-1, window_c * window_w * window_h))

def convolution(X, n_filters, kernel_size, padding, stride):

    global conv_activation_layer
    k_h = kernel_size[0]
    k_w = kernel_size[1]

    if padding == 'VALID':
        pad = 0
    else:
        pad = 1

    filters = []
    for i in range(n_filters):
        kernel = np.random.randn(k_h, k_w, X.shape[3])
        filters.append(kernel)
    kernel = np.reshape(filters, (k_h, k_w, X.shape[3], n_filters))

    n,h,w,c = X.shape[0], X.shape[1], X.shape[2], X.shape[3]
    filter_h, filter_w, filter_c, filter_n = kernel.shape[0], kernel.shape[1], kernel.shape[2], kernel.shape[3]

    out_h = (h + 2 * pad - filter_h) // stride + 1
    out_w = (w + 2 * pad - filter_w) // stride + 1

    X_flat = model.X_flatten(X, filter_h, filter_w, filter_c, out_h, out_w, stride, pad)
    W_flat = np.reshape(kernel, (filter_h * filter_w * filter_c, filter_n))

    z = np.matmul(X_flat, W_flat)
    z = np.transpose(np.reshape(z, (out_h, out_w, n, filter_n)), (2,0,1,3))

    conv_activation_layer = relu.activation(z)

    return conv_activation_layer


# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.

"""
Version 2.0
"""

import numpy as np

class CActivator(object):
    # z = 本层的wx+b计算值矩阵
    def forward(self, z):
        pass

    # z = 本层的wx+b计算值矩阵
    # a = 本层的激活函数输出值矩阵
    # delta = 上（后）层反传回来的梯度值矩阵
    def backward(self, z, a, delta):
        pass

# 直传函数，相当于无激活
class Identity(CActivator):
    def forward(self, z):
        return z

    def backward(self, z, a, delta):
        return delta, a

class Sigmoid(CActivator):
    def forward(self, z):
        a = 1.0 / (1.0 + np.exp(-z))
        return a

    def backward(self, z, a, delta):
        da = np.multiply(a, 1-a)
        dz = np.multiply(delta, da)
        return dz, da

class Tanh(CActivator):
    def forward(self, z):
        a = 2.0 / (1.0 + np.exp(-2*z)) - 1.0
        return a

    def backward(self, z, a, delta):
        da = 1 - np.multiply(a, a)
        dz = np.multiply(delta, da)
        return dz, da

class Relu(CActivator):
    def forward(self, z):
        a = np.maximum(z, 0)
        return a

    # 注意relu函数判断是否大于1的根据是正向的wx+b=z的值，而不是a值
    def backward(self, z, a, delta):
        da = np.zeros(z.shape)
        da[z>0] = 1
        dz = da * delta
        return dz, da


import random
import numpy as np
from collections import Counter

def generator(prob, N):

    return [0 if random.random() < prob else 1 for i in range(N)]

def entropy_exp(s):  # entropy by experience distribution. ex) s='0101..11'

    prb_0 = list(s).count('0') / len(s)
    prb_1 = 1.0 - prb_0
    if prb_0 == 1.0 or prb_1 == 1.0:
        ent_exp = 0.0
    else:
        ent_exp = - prb_0*np.log2(prb_0)- prb_1*np.log2(prb_1)

    return ent_exp

def coder(data, blk, ent, eps, rat):

    # binary data to symbols (= message)
    # ex) [0,1,1,0,1,0,0,1,...] -> ['0110','1001',...] (for blk = 4)
    message = [''.join(map(str,data[i:i+blk])) for i in range(0,len(data),blk)]

    # histogram
    syms = list(Counter(message).keys())
    frqs = list(Counter(message).values())
    prbs = [frqs[i]/len(message) for i in range(len(frqs))]
    hist = dict(zip(syms, prbs))

    # codebook
    index = 0
    digits = int(rat*blk)  # number of digits for each code
    codebook = {}
    for s,p in hist.items():
        ent_exp = entropy_exp(s)
        if abs(ent_exp - ent) <= eps and index < 2**digits-1:
            codebook[s] = '{:0{digits}b}'.format(index, digits=digits)
            index += 1
    codebook[False] = '{:0{digits}b}'.format(index, digits=digits)

    # coding (message -> codes)
    codes = [codebook[s] if s in codebook else codebook[False]
             for s in message]

    # decodebook
    decodebook = {}
    for k,v in codebook.items():
        decodebook[v] = k

    return (codes, decodebook)

def decoder(codes, decodebook, block):

    message = [decodebook[c] for c in codes]
    blocks = [list(s) if s != False else generator(0.5,block) for s in message]
    data_str = sum(blocks, [])

    return [int(d) for d in data_str]

def error_rate(data_in, data_out):

    N = len(data_in)
    count = 0
    for i in range(N):
        if data_in[i] != data_out[i]:
            count += 1

    return count / N

if __name__ == "__main__":

    # settings
    BLK = 100          # block size
    NUM = BLK * 1000  # number of binary data sequence
    PRB = 0.8         # probability to generate '0'
    ENT = - PRB * np.log2(PRB) - (1-PRB) * np.log2(1-PRB)  # entropy (= 0.7219..)
    RAT = 0.9         # transmission rate (RAT > ENT)
    EPS = 0.15         # eplsilon for typical sequence
    print("NUM = ", NUM)
    print("BLK = ", BLK)
    print("ENT = ", ENT)
    print("RAT = ", RAT)

    # generate data sequence
    data_in = generator(PRB, NUM)

    # code the data
    (codes, decodebook) = coder(data_in, BLK, ENT, EPS, RAT)

    # decode the data
    data_out = decoder(codes, decodebook, BLK)

    # evaluate error rate
    err_rate = error_rate(data_in, data_out)
    print("error rate = ", err_rate)


from math import ceil
import numpy as np
from jacobi import jacobi
from scipy.io import loadmat
# Finding the sim root directory
import sys
from pathlib import Path
cwd = Path.cwd()
for dirname in tuple(cwd.parents):
    if dirname.name == '3D-CG':
        sim_root_dir = dirname
        continue

gq_import_path = str(sim_root_dir.joinpath('master/gaussquad_import/gptsweights.mat'))

def gaussquad1d(pgauss):

    n = ceil((pgauss+1)/2)
    nth_leg_poly = jacobi(n, 0, 0)  # Computes the nth legendre polynomial

    n_roots = nth_leg_poly.roots()  # Points in the gauss quadrature scheme.

    A = np.zeros((n, n))

    for i in np.arange(n):
        A[i, :] = jacobi(i, 0, 0)(n_roots)

    rhs = np.zeros_like(A[:, 0])
    rhs[0] = 2
    weights = np.linalg.solve(A, rhs)

    x = (n_roots+1)/2   # Transformation from (-1, 1) -> (0, 1)
    w = weights/2    # Need to account for transformation from (-1, 1) -> (0, 1)

    return x, w

def gaussquad2d(pgauss):

    if pgauss <= 16:
        import os
        file = loadmat(gq_import_path)
        x = file['gpts2'][pgauss][0]
        w = np.squeeze(file['gweights2'][pgauss][1])

        return x, w
    else:

        # Get points and weights for integrating a 1D function exactly using gaussquad1d
        pts, weights = gaussquad1d(pgauss)
        weights = weights[:,None]   # Adding second dimension for later
        pts = 2*(pts) - 1     # Map from (0, 1) -> (-1, 1)
        weights *= 2        # Multiply by 2 because we stretched the domain by 2x

        # Map them to a square
        x, y = np.meshgrid(pts, pts, indexing='ij')     # Equivalent to matlab ngrid

        # List of gauss points on the master quad
        eta = np.concatenate((np.reshape(x, (-1, 1), order='F'),
                            np.reshape(y, (-1, 1), order='F')), axis=1)    # Specifying Fortran ordering to prevent having to sort at the end to get it to match the matlab ordering
        xi = np.copy(eta)
        xi[:, 0] = (1+xi[:, 0])*(1-xi[:, 1])/2 - 1  # See slide 25 in 16.930 Interpolation slides for forward transform from square to triangle. We now have a list of the gauss points as transformed to the master triangle, vertices at (-1, -1), (1, -1), (-1, 1)
        xi = (xi+1)/2   # Shifting and scaling to the master element: vertices at (0, 0), (1, 0), (0, 1)

        # Adjust weights due to the transformation
        w = weights*weights.T
        w = w.reshape((-1, 1))
        w *= (1-xi[:,1][:, None]) * (1/4)      # Multiply by (1-y) to scale weight by "jacobian" by the same scaling of a differential volumen element in the transformation from square -> triangle. In this case, the gauss pts are compressed as y changes.
        # Divided by 8 to reflect the 4x shrinkage of the triangle area and then 2x from square->triangle

        return xi, w

def gaussquad3d(pgauss):

    if pgauss <= 15:
        import os
        file = loadmat(gq_import_path)
        x = file['gpts3'][pgauss][0]
        w = np.squeeze(file['gweights3'][pgauss][1])
    else:
        raise NotImplementedError('GQ degree > 16 not supported!')

    return x, w

if __name__ == '__main__':
    import sys
    x, w = gaussquad2d(int(sys.argv[1]))

    print(x)
    print()
    print(w)

    # gaussquad1d(int(sys.argv[1]))


from __future__ import division
import numpy as np
import scipy.special as ss

def degtorad(d):
  """
    Convert degrees into radians.

    Parameters
    ----------
    d : float or array
        Angle in degrees.

    Returns
    -------
    Angle : float or array
        The angle converted into radians.
  """
  return (d/180.0)*np.pi

def radtodeg(r):
  """
    Convert radians into degrees.

    Parameters
    ----------
    d : float or array
        Angle in radians.

    Returns
    -------
    Angle : float or array
        The angle converted into degrees.
  """
  return (r/np.pi)*180.

def farat(x, y):
    """
    Compute ratio of factorials.

    Computes x!/y! via ln(x!) - ln(y!) to avoid
    numerical overflow.

    Parameters
    ----------
    x : int, float
        The factorial of x is the numerator
    y : int, float
        The factorial of y is the denumerator

    Returns
    -------
    Ratio : float
        The ratio x!/y! (not the logarithm).
    """
    lnr = ss.gammaln(x+1) - ss.gammaln(y+1)
    return np.exp(lnr)


import numpy as np

class LogisticLoss:
    @staticmethod
    def val(y, y_hat):
        return np.log(1 + np.exp(-y * y_hat))

    @staticmethod
    def prime(y, y_hat):
        return -y / (1 + np.exp(y * y_hat))

    @staticmethod
    def dprime(y, y_hat):
        a = np.exp(y * y_hat)
        return a / ((1 + a) ** 2)

class L2:
    @staticmethod
    def val(y, y_hat):
        return (y - y_hat)**2 / 2.

    @staticmethod
    def prime(y, y_hat):
        return y_hat - y

    @staticmethod
    def dprime(y, y_hat):
        return np.ones_like(y_hat)

class PseudoHuberLoss:

    def __init__(self, delta=1.0):
        self.delta = delta

    def val(self, y, y_hat):
        diff = y_hat - y
        return (self.delta ** 2) * (np.sqrt(1. + (diff / self.delta) ** 2) - 1.)

    def prime(self, y, y_hat):
        diff = y_hat - y
        return diff / np.sqrt(1. + (diff / self.delta) ** 2)

    def dprime(self, y, y_hat):
        diff = y_hat - y
        return np.power((1. + (diff / self.delta) ** 2), -1.5)


from statistics import mean
import numpy as np

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

def best_fit_slope(xs,ys):
    m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
         ((mean(xs)**2) - mean(xs**2)))
    return m

m = best_fit_slope(xs,ys)
print(m)


# Libraries
from random import shuffle, choice
import pandas as pd
import numpy as np

# Import Excel file into Python
file_name, sheet = "TSP.xlsx", "Arkusz1"
data = pd.read_excel(file_name, sheet_name = sheet, engine = 'openpyxl')

m = np.delete(data.to_numpy(), 0, 1) # The first arrays column removal, because it contains indexes

multistarts = 40 # Parameter 1
iterations1 = 1000 # Parameter 2
iterations2 = 750 # Parameter 3
neighbourhood_type = ""

solution = list(range(1, len(data) + 1)) # List for future job indexes from the file

def count_score(z): # Function responsible for counting distance
    time_travel, copied_solution = 0, z.copy()
    time_travel = np.double(time_travel)
    for n in range(0, len(solution) - 1):
        first_city, second_city = copied_solution[n], copied_solution[n + 1]
        time_travel += m[first_city - 1, second_city - 1]
    time_travel += m[copied_solution[0] - 1, copied_solution[-1] - 1]
    return time_travel

def pick_two_towns(): # Function responsible for picking two random jobs
    while True:
        t1, t2 = choice(solution), choice(solution) # Picking two random jobs
        if t1 != t2: # Checking if these jobs are not same
            break
    return t1, t2

def swap_method(x, y): # One of the neighbourhood types
    copied_solution = new_solution.copy() # Copied solution which is used for experiments
    copied_solution[x], copied_solution[y] = copied_solution[y], copied_solution[x] # Swapping positions of these jobs in list
    neighbourhood_type = "swap"
    return copied_solution, neighbourhood_type

def insertion_method(t1, y): # Second type of neighbourhood
    copied_solution = new_solution.copy() # Copied solution which is used for experiments
    copied_solution.remove(t1)
    copied_solution.insert(y, t1) # Inserting experimental solution with job 1 on the place of job 2
    neighbourhood_type = "insertion"
    return copied_solution, neighbourhood_type

score2 = 1000000000
solution2 = []
i = 0
while i != multistarts: # Multistart mechanism
    shuffle(solution) # Creating new solution (changing point) by shuffling jobs
    j = 0
    score = count_score(solution)
    while j != iterations1:
        min_solution = solution.copy() # Unchanged solution
        min_score, k = count_score(min_solution), 0
        while k != iterations2: # Testing neighbourhood
            new_solution = solution.copy()
            t1, t2 = pick_two_towns()
            x, y = solution.index(t1), solution.index(t2)  # Obtaining index of each job
            new_solution, neighbourhood_type = insertion_method(t1, y) # There can be also used swap method
            new_score = count_score(new_solution) # Calculating score of experimental solution
            if new_score < min_score:
                min_solution, min_score = new_solution.copy(), new_score # Prescribing new already changed solution
            k += 1
        if min_score < score: # Checking if the local solution is better than min_solution
            solution = min_solution # Applying changes to the solution
            score = min_score
        elif solution == min_solution: # Moment when local maximum is achieved
            break
        j += 1
    if score < score2: # Checking if proposed solution is better than previous one
        score2 = score
        solution2 = solution.copy()
    i += 1

print(f"The results of Hill Climbing algorithm with multistart mechanism for {file_name} file")
print(f"Solution: {solution2}")
print(f"Score: {round(score2,3)}")
print(f"Neighbourhood type: {neighbourhood_type}")
print(f"Multistarts: {multistarts}")
print(f"Iterations 1: {iterations1}")
print(f"Iterations 2: {iterations2}")


# Import the necessary modules
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# In this script, we will consider a receptor which can bind two ligands.
# Using the statistical mechanics in we learned in lecture, we can enumerate
# these states, figure out the multiplicity, and compute their statistical
# weights.  How does the probability of each state change with a concentration
# of ligand? Let's find out!

# We'll start by declaring some variables.
d_eb = -5  # Energetic difference between bound and unbound states (in kT)
d_ei = -2  # Energetic difference of interaction
L = np.logspace(-4, -1, 1000)  # Range of ligand concentration.

# To save our fingers some work, we'll define the partition function.
Z = 1 + 2 * L * np.exp(-d_eb) + L**2 * np.exp(-(2 * d_eb + d_ei))

# Now we'll compute the probability of each state
p_empty = 1 / Z  # Nothing bound -- empty receptor.
p_single = L * np.exp(-d_eb) / Z  # Probability of one ligand being bound.
p_double = L**2 * np.exp(-(2 * d_eb + d_ei)) / Z  # Probability of both bound.

# Now we'll plot the probabilites as a function of ligand concentration.
plt.figure()
plt.semilogx(L, p_empty, '-', label='empty')
plt.semilogx(L, p_single, '-', label='singly bound')
plt.semilogx(L, p_double, '-', label='doubly bound')

# Add labels as one always should.
plt.xlabel('ligand concentration (M)')
plt.ylabel('probability of state')
plt.legend()
plt.show()

# We can see that with a high concentration of ligand, the most common state
# is having both bound at once. In the middle of the range, empty and doubly
# bound are equal with a singly bound state less probable. Let's now compute
# the average number of bound ligands as a function of concentration.

# Since we have already enumerated our probabilities, the calculation is
# trivial.
n_bound = 2 * p_single + 2 * p_double

# Plot it!
plt.figure()
plt.plot(L, n_bound, 'k-')
plt.xlabel('ligand concentration (M)')
plt.ylabel('average number of bound ligands')
plt.show()

# Now go back and try changing the energy of interaction and see how the
# curves change!


# -*- coding: utf-8 -*-
"""
Created on Sun May  9 08:34:01 2021

@author: Raza_Jutt
"""

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

x = []
for i in range(0,201):
    x.append(i/100)

y=[]
for i in x:
    y.append(math.sqrt(math.cos(i)*math.cos(i) +1 ))

rand_x = []
rand_y = []
for i in range(0,10000):
    rand_x.append(random.uniform(0.00,2.00))
    rand_y.append(random.uniform(1.00,1.41))

xpoints = np.array(x)
ypoints = np.array(y)

x_coordinates = rand_x
y_coordinates = rand_y

plt.plot(xpoints, ypoints,'r')
plt.scatter(x_coordinates, y_coordinates,s=0.1, marker="x")
plt.show()

count = 0
for i in range(0,10000):
    x=rand_x[i]
    if math.sqrt(math.cos(x)*math.cos(x) +1 ) > rand_y[i] :
        count += 1

print(count/10000)


# Gradient Descent Implementation (fromt theory notebook)
# Demonstrated multi-collinearity problem from artificially generated dataset

# Code Flow:
    # 1. Import all relevant libraries.
    # 2. Generate sample data.
    # 3. Calculate weights using gradient descent.
    # 4. Plot the costs.
    # 5. Plot prediction vs actual.

# 1. Imports:
import numpy as np
import matplotlib.pyplot as plt

# 2. Generate sample data:
N = 10
D = 3
X = np.zeros((N, D))
X[:,0] = 1 # bias term
X[:5,1] = 1
X[5:,2] = 1
Y = np.array([0]*5 + [1]*5)

# 3. Calculate weights using gradient descent:
# w = np.linalg.solve(X.T.dot(X), X.T.dot(Y))
# won't work because of multicollinearity
# Hence gradient descent
costs = [] # keep track of squared error cost
w = np.random.randn(D) / np.sqrt(D) # randomly initialize w
learning_rate = 0.001

for t in range(1000): # 1000 iterations chosen
  # update w
  Yhat = X.dot(w)
  delta = Yhat - Y
  w = w - learning_rate*X.T.dot(delta)

  # find and store the cost
  mse = delta.dot(delta) / N # mean-sq error
  costs.append(mse) # append cost

# 4.Plot the costs:
plt.figure(1)
plt.plot(costs)
plt.xlabel('Iteration no.')
plt.ylabel('Cost')
plt.title('Change in cost')

# 5.Plot prediction vs actual
plt.figure(2)
plt.plot(Yhat, label='Prediction')
plt.plot(Y, label='Actual')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Actual vs Prediction - Gradient Descent')
plt.legend()
plt.show()

1.Linear Regression/1.Code - Using Theory/5.Gradient Descent.py


import get_root as gr
import numpy as np
from scipy.integrate import quad

#Part A
#integral of B_nu
def B_x_integrand(ln_x):
  x = np.exp(ln_x)
  tol = 1.0e-15
  B_denom = np.exp(x)-1  #denominator of the planck function
  exp_minus_1 = x + 0.5*(x**2) + (x**3)/6. #taylor expansion of denominator

  if( (np.fabs(B_denom - exp_minus_1) < tol) & (x<1)):
    Bx = (x**3)/exp_minus_1;
  else:
    Bx = (x**3)/B_denom

  return x * Bx  # x * dB / dlnx  * dlnx = dB/dx * dx

#Part B
#equation for finding max of dBdnu
def root_dBdnu(x):
  return x-3*(1-np.exp(-1*x))

#equation for finding max of dBdlambda
def root_dBdlambda(y):
  return y-5*(1-np.exp(-1*y));

#Part C
#equation for finding max of d2BdnudT
def root_d2BdnudT(z):
  return np.exp(z)*(z-4)+z+4;

def main():

    print("***** Question 2, Part A *****")

    #find the integral of B_nu dnu
    Bi, _ = quad(B_x_integrand,-32, 32)
    ans = np.pi**4 / 15.
    print('Normalized integral of B_nu dnu = %e.' % Bi)
    print('Fractional error in integral = %e.' % float((Bi-ans)/ans))

    print("***** Question 2, Part B *****")

    #find the maximum of dBdnu
    x_dBdnu = gr.get_root(root_dBdnu,3.)
    print('Maximum of dBdnu occurs at x = %e.' % x_dBdnu)

    #find the maximum of dBdlambda
    y_dBdlambda = gr.get_root(root_dBdlambda,3.)
    print('Maximum of dBdlambda occurs at y = %e.' % y_dBdlambda)

    print("***** Question 2, Part C *****")

    #find the maximum of d^2B/dnudT
    x_d2BdnudT = gr.get_root(root_d2BdnudT,4.)
    print('Maximum of d2BdnudT occurs at x = %e.' % x_d2BdnudT)

#execute the program
if __name__=='__main__':
    main()