import one_sample_t_test
import scipy.stats
import numpy as np

def paired_two_sample_t_test(
    samples_x, samples_y, diff: float = 0.0, tail: str = "both"
):
    """Paired two-sample t-test to check if two population means are different.

    Data is assumed to be paired, i.e. len(samples_x) = len(samples_y)
    and x_i has a direct relation with y_i, for all 1 <= i <= n. For
    example, each i can represent a different person, and x_i is the
    measure of its blood pressure before a medical treatment, and y_i is
    its blood pressure after the medical treatment.

    The paired two-sample t-test is just a regular one-sample t-test for
    the differences w_i = x_i - y_i ~ N(mu, sigma^{2}) of each sample group
    X and Y, where both mu and sigma are unknown parameters.

    Assumptions:
        i.i.d x_1, ..., x_n ~ N(mu_x, sigma_x^{2})
        i.i.d y_1, ..., y_n ~ N(mu_y, sigma_y^{2})
        x_i is paired (is related) to y_i.
        The differences w_i = x_i - y_i ~ N(mu, sigma^{2})
        Note 1: emphasizing that both samples has the same size.

    Test statistic: (w_mean - diff) / w_mean_std
    where:
        w_i = x_i - y_i;
        w_mean = mean(w);
        w_mean_std = w_std / sqrt(n);
        `diff` is given as a user argument.

    Null distribution: W ~ t(n - 1), where t is the t-student distribution.

    H0: x_mean - y_mean = diff
    HA:
        if tail = `both` : x_mean - y_mean != diff;
        if tail = `left` : x_mean - y_mean < diff;
        if tail = `right`: x_mean - y_mean > diff.
    """
    assert len(samples_x) == len(samples_y)

    samples_x = np.asfarray(samples_x)
    samples_y = np.asfarray(samples_y)

    return one_sample_t_test.t_test(
        samples_x - samples_y, hypothesis_mean=diff, tail=tail
    )

def _test():
    sample_std = 6
    test_x = [12, 10, -5, -5, 0, 0, 0]
    test_y = [12, 11, -5, -4, 0, 1, -1]
    diffs = [0, 0, 0, -1, 0, -1, 1]
    for tail, tail_scipy in zip(
        ["both", "left", "right"], ["two-sided", "less", "greater"]
    ):
        for mean_x, mean_y, diff in zip(test_x, test_y, diffs):
            sample_x = mean_x + sample_std * np.random.randn(200)
            sample_y = mean_y + sample_std * np.random.randn(200)
            res = paired_two_sample_t_test(sample_x, sample_y, diff=diff, tail=tail)
            print(tail, res)
            if np.isclose(0, diff):
                assert np.allclose(
                    res,
                    scipy.stats.ttest_rel(sample_x, sample_y, alternative=tail_scipy),
                )

if __name__ == "__main__":
    _test()


import sympy as sy
import numpy as np
import random
from sympy import *
import FunctionApproximation as approx
import scipy.optimize
import scipy
import CustomPlots
#from sympy.mpmath import *

def degToRad(deg):
	rad = deg/360.0
	return rad

def make2dList(rows, cols):
	a=[]
	for row in xrange(rows): a += [[0]*cols]
	return a

def variableSymbols(variables):
	if variables:
		variableSymbols = []
		if isinstance(variables[0],str) == True:
			for variable in variables:
				variableSymbols.append(symbols(variable))
		else:
			variableSymbols = variables

	return variableSymbols

def expressionSymbols(expression):

	if isinstance(expression, str):
		expression = sy.sympify(expression)
	return expression

def evaluateExpression(expr, variables = [], values = [],**kwargs):
	result = 0
	#print(variables)
	#print(values)

	if isinstance(expr,str):
		expr = sy.sympify(expr)

	if len(variables) != 0 and len(values) != 0:
		variableSymbol = []
		if isinstance(variables[0],str) == True:
			for variable in variables:
				variableSymbol.append(symbols(variable))
		else:
			variableSymbols = variables

		subsList = []
		for variable, value in zip(variables, values):
			subsList.append((variable,value))
		substitutedFunction = expr.subs(subsList)
		result = substitutedFunction.evalf()
	else:
		result = expr.evalf(subs=kwargs)

	return result

def getGradientExpression(expression,variables):
	# Function accepts string or expression types, and list of variable strings
	# Returns list of partial derivatives of function with repsect to variables given in list
	variableSymbol = []

	if isinstance(variables[0],str):
		variableSymbol = []
		for variable in variables:
			variableSymbol.append(symbols(variable))
	else:
		variableSymbol = variables

	#if isinstance(expression, str):
	expression = sy.sympify(expression)

	partialFunctions = []
	for variable in variableSymbol:
		partialFunctions.append(sy.diff(expression,variable))
	return partialFunctions

def getGradient(expression,variables,variableValues,normalize=False):
	# Inputs: expression is a text string or sympy expression for the objective function
	# Variables is a list of text inputs for each input variable
	variables = variableSymbols(variables)

	partials = getGradientExpression(expression,variables)

	slopeValues = []
	for partial in partials:
		slopeValues.append(evaluateExpression(partial, variables=variables, values = variableValues))

	normalizedSlopes = []
	slopeList = slopeValues
	if normalize == True:
		for slopeValue in slopeValues:
			normalizedSlopes.append(slopeValue/totalSlope)
		slopeList = normalizedSlopes

	return slopeList

def getNumGradient(expression,variables,variableValues,normalize=False,delta = 0.001):
	startingValue = evaluateExpression(expression, variables=variables, values = variableValues)
	slopeList = []
	for v in range(0,len(variables)):
		newVariableValue = variableValues[v] + delta
		testValueSet = list(variableValues)
		testValueSet[v] = newVariableValue
		testFunctionValue = evaluateExpression(expression, variables=variables, values = testValueSet)
		slopeList.append((testFunctionValue-startingValue)/delta)

	return slopeList

def hessian(expression,variables):
	n = len(variables)
	H = make2dList(n, n)
	# String conversions and typecasting
	variableSymbol = []
	if isinstance(variables[0],str):
		variableSymbol = []
		for variable in variables:
			variableSymbol.append(symbols(variable))
	else:
		variableSymbol = variables
	if isinstance(expression, str):
		expression = sy.sympify(expression)
	# Core iteration function
	for i in range(0,n):
		firstPartial = diff(expression,variableSymbol[i])
		for j in range(0,n):
			if i > j:
				H[i][j] = H[j][i]
			else:
				H[i][j] = diff(firstPartial,variableSymbol[j])

	return H

def steepestDescentMinimum(expression,variables,startingPoint,epsilon=0.0001,nMax=100,damping=1,echo=False,**kwargs):
	# Inputs: expression is a text string or sympy expression for the objective function
	# Variables is a list of text inputs for each input variable
	# StartingPoint is a vector of intial points for each input variable
	# Convergence and timeout parameters are optional
	alpha = [0,0.1,0.2]
	i = 0
	if isinstance(expression, str):
		expression = sy.sympify(expression)

	# Loop
	shouldContinue = True
	position = startingPoint
	objectiveValue = evaluateExpression(expression, variables = variables, values = position)
	#print("F = %2.6f" % (objectiveValue))
	# print("About to start loop")
	# Print current iteration results
	if echo == True:
		headerString = "Iteration\t"
		for variable in variables:
			headerString += "%s\t" % (variable)
		headerString += "Gradient\t"
		headerString += "F(x)"
		print(headerString)

	while shouldContinue == True:
		i = i+1
		#print("Total Iterations should be  %i" %(nMax))
		#print("Iteration %i" %(i))
		# Get gradient at position
		# print("About to get gradient")
		slopeList = getGradient(expression,variables,position,normalize=True)
		#print("Slope values from getGradient")
		#print(slopeList)
		# print("About to fit polynomial")
		# Get three points in that direction at intervals of 0.5,1,2
		functionValues = [objectiveValue]
		for alphaValue in alpha:
			if alphaValue != alpha[0]:
				testLocation = []
				for oldPosition, slope in zip(position,slopeList):
					testLocation.append(oldPosition+slope*alphaValue)
				functionValues.append(evaluateExpression(expression, variables = variables, values = testLocation))
		# Fit parabola to curve
		C = approx.threePointQuadraticApprox(alpha, functionValues)
		# Check parabola is concave up
		# Calculate alpha that gives minimum
		alphaStar = 0.0
		if C[2] < 0:
			print("Fitted parabola is concave down. Minimum alpha value is not bounded.")
			alphaStar = 1
		else:
			(alphaStar,bestY) = minimizeParabola(C)
		# Move to position of calculated alpha
		newPosition = []
		for oldPosition, slope in zip(position,slopeList):
			alphaStar = alphaStar*damping
			newPosition.append(oldPosition+slope*alphaStar)
		lastPosition = position
		position = newPosition
		objectiveValueLast = objectiveValue
		objectiveValue = evaluateExpression(expression, variables = variables, values = position)

		# Print current iteration results
		if echo == True:
			resultsString = "%i        \t" %(i)
			for value in position:
				resultsString += "%2.4f\t" % (value)
			resultsString += "{}\t".format(slopeList)
			resultsString += "%2.6f" % (objectiveValue)
			print(resultsString)

		# Check convergence
		deltaObjective = objectiveValueLast - objectiveValue
		#print("Delta Objective = %2.4f" % (float(deltaObjective)))
		if abs(deltaObjective) <= epsilon:
			shouldContinue = False
			print("Local Optimium found")

		#print("About to check iteration maximum")
		if i > nMax:
			print("Function timed out. Returning final result")
			shouldContinue = False

	print("#### - Results - ####")
	for variable, variableValue in zip(variables,position):
		print(variable + " = %2.6f" % (variableValue))
	print("F = %2.6f" % (objectiveValue))
	return (objectiveValue, position)

def SLP(expression,variables,startingPoint,inequalityConstraints=[],epsilon=0.0001,nMax=100,stepMax = 0.5,saveSequence=False,echo=False):
	# Outputs: optimum position
	if isinstance(expression, str):
		expression = sy.sympify(expression)

	shouldContinue = True
	position = startingPoint
	objectiveValue = evaluateExpression(expression, variables = variables, values = position)
	if echo == True:
		headerString = "Iteration\t"
		for variable in variables:
			headerString += "%s\t" % (variable)
		headerString += "F(x)"
		print(headerString)
	designSequence = [position]

	n = 0
	taylorCoeffs = [0]*len(inequalityConstraints)
	b = [0]*len(inequalityConstraints)

	while shouldContinue == True:
		n = n + 1
		oldPosition = list(position)

		# Linearize objective function and constraints
		(expressionCoeffs,intercept) = approx.taylorLinearize(expression,variables = variables, values = position)
		expressionCoeffs = np.array(expressionCoeffs)
		for i in range(0,len(inequalityConstraints)):
			(taylorCoeffs[i],b[i]) = approx.taylorLinearize(inequalityConstraints[i],variables = variables, values = position)

		taylorArray = np.array(taylorCoeffs)
		# Solve linear problem
		res = scipy.optimize.linprog(expressionCoeffs,A_ub=taylorArray,b_ub=b)
		#print(res)
		# Exctract optimum from result
		newOptimum = res.get("fun", -9999)
		objectiveValueLast = objectiveValue
		objectiveValue = newOptimum
		# Extract optimized design from result
		newPosition  = res.get("x",[-9999]*len(variables))
		newPosition = newPosition.tolist()

		# Check movement delta for each variable
		for i in range(0,len(variables)):
			delta =  newPosition[i] - position[i]
			if abs(delta)> stepMax:
				#print("New position is a large move")
				#print("Former position: " + str(position[i]))
				position[i] = position[i] + delta/abs(delta)*stepMax
				#print("Optimum position: " + str(newPosition[i]))
				#print("Chosen move position: " + str(position[i]))
			else:
				position[i] = newPosition[i]
				#print("New position is a valid move")
		designSequence.append(list(position))

		# Check convergence
		# Print current iteration results
		if echo == True:
			resultsString = "%i        \t" %(n)
			for value in position:
				resultsString += "%2.4f\t" % (value)
			resultsString += "%2.6f" % (objectiveValue)
			print(resultsString)

		# Check convergence
		deltaObjective = objectiveValueLast - objectiveValue
		#print("Last position: " + str(oldPosition))
		#print("Current position: " + str(position))
		variableDeltas = [abs(old - new) for old, new in zip(oldPosition,position)]
		#print(variableDeltas)
		deltaVar = max(variableDeltas)
		#print("Delta Objective = %2.4f" % (float(deltaObjective)))
		if (abs(deltaObjective) <= epsilon and deltaVar <= epsilon):
			shouldContinue = False
			print("Local Optimium found")

		#print("About to check iteration maximum")
		if n > nMax:
			print("Function timed out. Returning final result")
			shouldContinue = False

	print("#### - Results - ####")
	for variable, variableValue in zip(variables,position):
		print(variable + " = %2.6f" % (variableValue))
	print("F = %2.6f" % (objectiveValue))

	if saveSequence == True:
		print(designSequence)
		designSequence = np.array(designSequence)
		x = np.arange(0,3,0.1)
		y = np.arange(0,3,0.1)
		z = make2dList(len(y),len(x))
		constraintValues = []
		for i in range(0,len(inequalityConstraints)):
			constraintValues.append(make2dList(len(y),len(x)))
		for i in range(0,len(x)):
			for j in range(0,len(y)):
				z[j][i] = evaluateExpression(expression,variables = variables,values = [x[i],y[j]])
				for n in range(0,len(inequalityConstraints)):
					constraintValues[n][j][i] = evaluateExpression(inequalityConstraints[n],variables = variables,values = [x[i],y[j]])
		CustomPlots.plotConstrainedContour(x,y,z,"DesignSequence",constraints=constraintValues,lineArray = designSequence)

	return (objectiveValue, position)

def augmentedLagrange(expression,variables,equalityConstraints = [], x0 = [],l0 = 0,epsilon=0.0001,nMax=100,damping=1.0,rp=1.0,echo=False,**kwargs):
	# Inputs: expression is a text string or sympy expression for the objective function
	# Variables is a list of text inputs for each input variable
	# StartingPoint is a vector of intial points for each input variable
	# Convergence and timeout parameters are optional
	alpha = [0,1,2]
	i = 0
	l = [l0]*len(equalityConstraints)
	constraintValues = [0]*len(equalityConstraints)

	if isinstance(expression, str):
		expression = sy.sympify(expression)
	objectiveExpression = expression

	equalityConstraints = [sy.sympify(constraint) for constraint in equalityConstraints]

	#if len(x0) == 0:
		# Calculate starting point from initial lagrange function

	# Loop
	shouldContinue = True
	position = x0
	objectiveValue = evaluateExpression(expression, variables = variables, values = position)
	#print("F = %2.6f" % (objectiveValue))
	# print("About to start loop")
	# Print current iteration results
	if echo == True:
		headerString = "Iteration\t"
		for variable in variables:
			headerString += "%s\t" % (variable)
		for i in range(0,len(l)):
			headerString += "L%i\t" % (i+1)
		for i in range(0,len(equalityConstraints)):
			headerString += "h%i\t" % (i+1)
		headerString += "Phi(x)"
		print(headerString)

	n = 0
	while shouldContinue == True:
		n = n+1

		# Construct expression for augmented lagrange function

		expression = objectiveExpression
		for i in range(0,len(equalityConstraints)):
			expression = expression + rp*equalityConstraints[i]*equalityConstraints[i] + l[i]*equalityConstraints[i]
		#print(expression)

		slopeList = getGradient(expression,variables,position,normalize=False)

		# Get three points in that direction at intervals of 0,0.1,0.2
		functionValues = [objectiveValue]
		for alphaValue in alpha:
			if alphaValue != alpha[0]:
				testLocation = []
				for oldPosition, slope in zip(position,slopeList):
					testLocation.append(oldPosition+slope*alphaValue)
				functionValues.append(evaluateExpression(expression, variables = variables, values = testLocation))
		# Fit parabola to curve
		C = approx.threePointQuadraticApprox(alpha, functionValues)
		# Check parabola is concave up
		# Calculate alpha that gives minimum
		alphaStar = 0.0
		if C[2] < 0:
			print("Fitted parabola is concave down. Minimum alpha value is not bounded.")
			alphaStar = 0.1
		else:
			(alphaStar,bestY) = minimizeParabola(C)
		# Move to position of calculated alpha
		newPosition = []
		for oldPosition, slope in zip(position,slopeList):
			newPosition.append(oldPosition+slope*damping*alphaStar)
		lastPosition = position
		position = newPosition
		objectiveValueLast = objectiveValue
		objectiveValue = evaluateExpression(expression, variables = variables, values = position)

		# Update lagrange multipliers
		for i in range(0,len(equalityConstraints)):
			constraintValues[i] = evaluateExpression(equalityConstraints[i], variables = variables, values = position)
			l[i] = l[i] + 2*rp*constraintValues[i]

		# Print current iteration results
		if echo == True:
			resultsString = "%i        \t" %(n)
			for value in position:
				resultsString += "%2.4f\t" % (value)
			for value in l:
				resultsString += "%2.4f\t" % (value)
			for value in constraintValues:
				resultsString += "%2.4f\t" % (value)
			resultsString += "%2.6f" % (objectiveValue)
			print(resultsString)

		# Check convergence
		deltaObjective = objectiveValueLast - objectiveValue
		#print("Delta Objective = %2.4f" % (float(deltaObjective)))
		if abs(deltaObjective) <= epsilon:
			shouldContinue = False
			print("Local Optimium found")

		#print("About to check iteration maximum")
		if n > nMax:
			print("Function timed out. Returning final result")
			shouldContinue = False

	print("#### - Results - ####")
	for variable, variableValue in zip(variables,position):
		print(variable + " = %2.6f" % (variableValue))
	print("F = %2.6f" % (objectiveValue))
	return (objectiveValue, position)

def quasiNewtonMinimization(expression,variables, startingPoint,epsilon=0.0001,nMax=100,method='bfgs',echo=False):
	x = startingPoint
	i = 0
	shouldContinue = True
	n = len(variables)
	A = np.identity(n)
	alphaTestPoints = [0,0.1,0.2]
	fTestPoints = [0,0,0]
	f = 99999
	delFOld = np.asarray([0,0])
	expression = expressionSymbols(expression)
	variables = variableSymbols(variables)
	xNew = [0,0]
	xOld = [0,0]
	if echo == True:
		headerString = "Iteration \t"
		for variable in variables:
			headerString += str(variable) + "      \t"
		headerString += "F(x)"
		print(headerString)

	while shouldContinue == True:
		(slope, delF) = getGradient(expression,variables,x)
		delF = [-delElement for delElement in delF] # Look downhill rather than uphill
		delF = np.asarray(delF)

		# Calculate values for alpha Star
		j = 0
		for alphaTest in alphaTestPoints:
				xTestPoint = x + alphaTest*(np.dot(A,delF))
				fTestPoints[j] = evaluateExpression(expression,variables = variables,values = xTestPoint)
				j = j + 1

		C = approx.threePointQuadraticApprox(alphaTestPoints,fTestPoints)

		# Check parabola is concave up
		# Calculate alpha that gives minimum
		alphaStar = 0.0
		if C[2] < 0:
			print("Fitted parabola is concave down. Minimum alpha value is not bounded.")
			alphaStar = 0.1
		else:
			(alphaStar,bestY) = minimizeParabola(C)
		xNew = x + alphaStar*(np.dot(A,delF))
		xOld = x
		x = xNew

		# Calculate new A matrix
		if method == 'bfgs':
			p = [xElement - xOldElement for xElement, xOldElement in zip(x, xOld)] # Nx1 vector
			y = delF - delFOld # Nx1 vector
			sigma = np.dot(np.transpose(p),y) # Scalar
			tau = np.dot(np.dot(np.transpose(y),A),y) # Scalar
			D = (sigma+tau)/(sigma*sigma)*np.dot(p,np.transpose(p)) - 1/sigma*(np.dot(np.dot(A,y),np.transpose(p)) + np.dot(p,np.transpose(np.dot(A,y))))
			A = A + D
		elif method == 'DFP':
			print("Implementation of DFP still needed")
		else:
			print("No method selected in quasiNewtonMinimization")

		fNew = evaluateExpression(expression,variables = variables,values = x)
		fDelta = f - fNew
		f = fNew
		delFOld = delF
		i = i + 1

		# Print current iteration results
		if echo == True:
			resultsString = "%i       \t" %(i)
			for variable, value in zip(variables,x):
				resultsString += "%2.4f  \t" % (value)
			resultsString += "%2.6f\t" % (f)
			print(resultsString)

		# Check convergence
		if abs(fDelta) < epsilon:
			shouldContinue = False
			print("Local Optimium found")
		if i > nMax:
			print("Function timed out. Returning final result")
			shouldContinue = False

	print("#### - Results - ####")
	for variable, variableValue in zip(variables,x):
			print(str(variable) + " = %2.6f" % (variableValue))
	print("F = %2.6f" % (f))

	return (f, x)

def NewtonRaphson1DFindZeroUnconstrained(functionString,xStart,tolerance=0.0001,maxIterations=100,echo=False):
	xSymbolic = symbols('x')
	objectiveExpression = sy.sympify(functionString)
	#print(objectiveExpression)
	objectivePrime = diff(objectiveExpression, xSymbolic)
	#print(objectivePrime)

	x = xStart
	shouldContinue = True
	epislon = 1000
	i = 0
	if echo==True:
		print("Iter \t X     \t F      \tF'")

	while shouldContinue == True:
		i = i + 1
		f = evaluateExpression(objectiveExpression, [xSymbolic],[x])
		fPrime = evaluateExpression(objectivePrime, [xSymbolic],[x])
		xNew = x - f/fPrime
		#print(f)
		#print(fPrime)

		epsilon = abs(xNew - x)

		x = xNew

		if epsilon <= tolerance or i >= maxIterations:
			shouldContinue = False
		if echo==True:
			print("%i \t %2.4f \t %2.4f \t %2.4f" % (i, x,f,fPrime))

	return x

def NewtonRaphson1DFindMinUnconstrained(functionString,xStart,tolerance=0.0001,maxIterations=100):
	xSymbolic = symbols('x')
	objectiveExpression = sy.sympify(functionString)
	#print(objectiveExpression)
	objectivePrime = diff(objectiveExpression, xSymbolic)
	#print(objectivePrime)
	objectiveDoublePrime = diff(objectivePrime, xSymbolic)
	#print(objectiveDoublePrime)
	x = xStart
	shouldContinue = True
	epislon = 1000
	i = 0

	while shouldContinue == True:
		i = i + 1
		f = evaluateExpression(expression = objectiveExpression, x = x)
		fPrime = evaluateExpression(expression = objectivePrime, x = x)
		fDoublePrime = evaluateExpression(expression = objectiveDoublePrime, x = x)
		xNew = x - fPrime/fDoublePrime
		#print(f)
		#print(fPrime)

		epsilon = abs(xNew - x)

		x = xNew

		if epsilon <= tolerance or i >= maxIterations:
			shouldContinue = False
		print("Iteration = %i, X = %2.4f, F = %2.4f, F' = %2.4f" % (i, x,f,fPrime))

	error = evaluateExpression(expression = objectiveExpression, x = x)

	return x

def evaluateExteriorPenalty(expression, inequalityConstraints=[], equalityConstraints=[], variables = [], values = [], rp=1, evaluate=True):
	# returns either a floating point value or a sympify expression valid at the location selected
	inequalityExpressions = [sy.sympify(constraint) for constraint in inequalityConstraints]
	equalityExpressions = [sy.sympify(constraint) for constraint in equalityConstraints]
	if isinstance(expression, str):
		expression = sy.sympify(expression)

	if evaluate == True:
		if variables and values:
			objectiveValue = evaluateExpression(expression, variables = variables, values = values)
		else:
			print('Cannot evaluate Exterior Penalty function without both variables and values')

		constraintValue = 0
		n = len(inequalityConstraints)
		if n > 0:
			for constraint in inequalityExpressions:
				constraintHere = evaluateExpression(constraint, variables = variables, values = values)
				newConstraintValue = max(0,constraintHere)**2
				constraintValue = constraintValue + newConstraintValue

		m = len(equalityConstraints)
		if m > 0:
			for constraint in equalityExpressions:
				newConstraintValue = evaluateExpression(constraint, variables = variables, values = values)**2
				constraintValue = constraintValue + newConstraintValue

		totalValue = objectiveValue + rp * constraintValue
		result = totalValue
	else:
		constraintString = ''
		n = len(inequalityConstraints)
		if n > 0:
			for i in range(0,n):
				newConstraintValue = evaluateExpression(inequalityExpressions[i], variables = variables, values = values)
				if newConstraintValue > 0:
					if constraintString == '':
						constraintString = constraintString + '(' + inequalityConstraints[i] + ')**2'
					else:
						constraintString = constraintString + ' + (' + inequalityConstraints[i] + ')**2'

		m = len(equalityConstraints)
		if m > 0:
			for j in range(0,m):
				newConstraintValue = evaluateExpression(equalityExpressions[j], variables = variables, values = values)
				if constraintString == '':
					constraintString = constraintString + '(' + equalityConstraints[j] + ')**2'
				else:
					constraintString = constraintString + '+ (' + equalityConstraints[j] + ')**2'

		constraintString = 'rp * (' + constraintString + ')'
		returnString = expression + sy.sympify(constraintString)
		returnString.subs(symbols('rp'),rp)

		result = returnString

	return result

def evaluateLinearExtendedPenalty(expression, inequalityConstraints=[], equalityConstraints=[], variables = [], values = [], rp=1.0, epsilon = -9999, evaluate=True):
	# returns either a floating point value or a sympify expression valid at the location selected

	if epsilon == -9999:
		epsilon = -0.2*np.sqrt(1/rp)

	rpPrime = 1/rp

	inequalityExpressions = [sy.sympify(constraint) for constraint in inequalityConstraints]
	equalityExpressions = [sy.sympify(constraint) for constraint in equalityConstraints]

	if isinstance(expression, str):
		expression = sy.sympify(expression)

	if evaluate == True:
		if variables and values:
			objectiveValue = evaluateExpression(expression, variables = variables, values = values)
		else:
			print('Cannot evaluate Exterior Penalty function without both variables and values')

		inconstraintValue = 0
		n = len(inequalityConstraints)
		if n > 0:
			for constraint in inequalityExpressions:
				newConstraintValue = evaluateExpression(constraint, variables = variables, values = values)
				if newConstraintValue > epsilon:
					inconstraintValue = inconstraintValue - (2*epsilon - newConstraintValue)/epsilon**2
				else:
					inconstraintValue = inconstraintValue - 1/newConstraintValue

		constraintValue = 0
		m = len(equalityConstraints)
		if m > 0:
			for constraint in equalityExpressions:
				newConstraintValue = evaluateExpression(constraint, variables = variables, values = values)**2
				constraintValue = constraintValue + newConstraintValue

		totalValue = objectiveValue + inconstraintValue/rp + constraintValue*rp
		result = totalValue
	else:
		inconstraintString = ''
		n = len(inequalityConstraints)
		if n > 0:
			for i in range(0,n):
				newConstraintValue = evaluateExpression(inequalityExpressions[i], variables = variables, values = values)
				if newConstraintValue > epsilon:
					inconstraintString = inconstraintString + '- (2*%f - ('%(epsilon) + str(inequalityConstraints[i]) + '))/(%f**2)'%(epsilon)
				else:
					inconstraintString = inconstraintString + '- 1/(' + str(inequalityConstraints[i]) + ')'
		if inconstraintString == '':
			inconstraintString = '0'

		eqConstraintString = ''
		m = len(equalityConstraints)
		if m > 0:
			for j in range(0,m):
				newConstraintValue = evaluateExpression(equalityExpressions[j], variables = variables, values = values)
				if newConstraintValue > 0:
					eqConstraintString = eqConstraintString + '+ (' + equalityConstraints[j] + ')**2'
		if eqConstraintString == '':
			eqConstraintString = '0'

		#constraintString = '%f * ('%(rp) + eqConstraintString + ') + (%f)/('%(rpPrime) + inconstraintString + ')'
		returnString = expression + rp*sy.sympify(eqConstraintString) + rpPrime*sy.sympify(inconstraintString)
		result = returnString

	return result

def evaluateInteriorInverseBarrier(expression, inequalityConstraints=[], equalityConstraints=[], variables = [], values = [], rp=1.0, evaluate=True):
	# returns either a floating point value or a sympify expression valid at the location selected

	inequalityExpressions = [sy.sympify(constraint) for constraint in inequalityConstraints]
	equalityExpressions = [sy.sympify(constraint) for constraint in equalityConstraints]

	if isinstance(expression, str):
		expression = sy.sympify(expression)

	if evaluate == True:
		if variables and values:
			objectiveValue = evaluateExpression(expression, variables = variables, values = values)
		else:
			print('Cannot evaluate Interior Inverse Barrier function without both variables and values')

		inConstraintValue = 0
		n = len(inequalityConstraints)
		if n > 0:
			for constraint in inequalityExpressions:
				newConstraintValue = evaluateExpression(constraint, variables = variables, values = values)
				if newConstraintValue <= 0:
					inConstraintValue = inConstraintValue - 1/newConstraintValue
				else:
					inConstraintValue = inConstraintValue + 100*rp * newConstraintValue

		m = len(equalityConstraints)
		eqConstraintValue = 0
		if m > 0:
			for constraint in equalityExpressions:
				newConstraintValue = evaluateExpression(constraint, variables = variables, values = values)**2
				eqConstraintValue = eqConstraintValue + newConstraintValue

		totalValue = objectiveValue + inConstraintValue/rp + rp * eqConstraintValue
		result = totalValue
	else:
		inConstraintString = ''
		n = len(inequalityConstraints)
		if n > 0:
			for i in range(0,n):
				newConstraintValue = evaluateExpression(inequalityExpressions[i], variables = variables, values = values)
				if newConstraintValue <= 0:
					inConstraintString = inConstraintString + '- 1/(' + str(inequalityConstraints[i]) + ')'
				else:
					inConstraintString = inConstraintString + ' + 100*%f*('%(rp) + str(inequalityConstraints[i]) + ')'

		eqConstraintString = ''
		m = len(equalityConstraints)
		if m > 0:
			for j in range(0,m):
				newConstraintValue = evaluateExpression(equalityExpressions[j], variables = variables, values = values)
				if newConstraintValue > 0:
					eqConstraintString = eqConstraintString + '+ (' + equalityConstraints[j] + ')**2'
		else:
			eqConstraintString = eqConstraintString + '0'
		constraintString = '%f * ('%(rp) + eqConstraintString + ') + (' + inConstraintString + ')/%f'%(rp)
		returnString = expression + sy.sympify(constraintString)
		result = returnString

	return result

def constrainedMinimum(expression,variables,startingPoint=[],inequalityConstraints=[],equalityConstraints=[],rp=1,method='ExteriorPenalty',echo=False,damping=1,epsilon=0.0001,nMax=100,alpha = [0,0.1,0.2],printResults=True):

	# Method options: 'ExteriorPenalty', 'InteriorPenalty', 'InteriorInverseBarrier','InverseLog', 'InteriorLinearExtended', 'QuadraticExtended'
	rpSymbol = symbols("rp")

	i = 0
	if isinstance(expression, str):
		expression = sy.sympify(expression)

	if len(startingPoint) == 0:
		startingPoint = [0] * len(variables)

	# Loop
	shouldContinue = True
	position = startingPoint
	if method == 'ExteriorPenalty':
		objectiveValue = evaluateExteriorPenalty(expression,
			inequalityConstraints=inequalityConstraints,
			equalityConstraints=equalityConstraints,
			variables = variables,
			values = position,
			rp = rp)
	elif method == 'InteriorLinearExtended':
		objectiveValue = evaluateLinearExtendedPenalty(expression,
			inequalityConstraints=inequalityConstraints,
			equalityConstraints=equalityConstraints,
			variables = variables,
			values = position,
			rp = rp,
			epsilon = -9999,
			evaluate=True)
	elif method == 'InteriorInverseBarrier':
		objectiveValue = evaluateInteriorInverseBarrier(expression,
			inequalityConstraints=inequalityConstraints,
			equalityConstraints=equalityConstraints,
			variables = variables,
			values = position,
			rp = rp)
	else:
		print('The method ' + method + ' is not implemented yet.')

	if echo == True:
		headerString = "Iteration\t"
		for variable in variables:
			headerString += "%s\t" % (variable)
		headerString += "Gradient\t"
		headerString += "F(x)"
		print(headerString)

	while shouldContinue == True:
		i = i+1

		#print("Total Iterations should be  %i" %(nMax))
		#print("Iteration %i" %(i))
		# Get gradient at position
		# print("About to get gradient")
		if method == 'ExteriorPenalty':
			expressionHere = evaluateExteriorPenalty(expression,
				inequalityConstraints=inequalityConstraints,
				equalityConstraints=equalityConstraints,
				variables = variables,
				values = position,
				rp = rp,
				evaluate=False)
		elif method == 'InteriorLinearExtended':
			expressionHere = evaluateLinearExtendedPenalty(expression,
				inequalityConstraints=inequalityConstraints,
				equalityConstraints=equalityConstraints,
				variables = variables,
				values = position,
				rp = rp,
				epsilon = -9999,
				evaluate=False)
		elif method == 'InteriorInverseBarrier':
			expressionHere = evaluateInteriorInverseBarrier(expression,
				inequalityConstraints=inequalityConstraints,
				equalityConstraints=equalityConstraints,
				variables = variables,
				values = position,
				rp = rp,
				evaluate = False)
		else:
			print('The method ' + method + ' is not implemented yet.')
			return

		expressionHere = expressionHere.subs(rpSymbol,float(rp))
		#print(expressionHere)
		#print(variables)
		#print(position)
		slopeList = getGradient(expressionHere,variables,position,normalize=False)
		#print("Slope values from getGradient")
		#print(slopeList)
		# print("About to fit polynomial")
		# Get three points in that direction at intervals of 0.5,1,2
		functionValues = [objectiveValue]
		for alphaValue in alpha:
			if alphaValue != alpha[0]:
				testLocation = []
				for oldPosition, slope in zip(position,slopeList):
					#print(oldPosition)
					#print(slope)
					#print(alphaValue)
					testLocation.append(oldPosition-slope*alphaValue)

				if method == 'ExteriorPenalty':
					functionValues.append(evaluateExteriorPenalty(expression,
						inequalityConstraints=inequalityConstraints,
						equalityConstraints=equalityConstraints,
						variables = variables,
						values = testLocation,
						rp = rp))
				elif method == 'InteriorLinearExtended':
				   functionValues.append(evaluateLinearExtendedPenalty(expression,
						inequalityConstraints=inequalityConstraints,
						equalityConstraints=equalityConstraints,
						variables = variables,
						values = testLocation,
						rp = rp,
						epsilon = -9999))
				elif method == 'InteriorInverseBarrier':
					functionValues.append(evaluateInteriorInverseBarrier(expression,
						inequalityConstraints=inequalityConstraints,
						equalityConstraints=equalityConstraints,
						variables = variables,
						values = testLocation,
						rp = rp))
				else:
					print('The method ' + method + ' is not implemented yet.')

		# Fit parabola to curve
		C = approx.threePointQuadraticApprox(alpha, functionValues)
		# Check parabola is concave up
		# Calculate alpha that gives minimum
		alphaStar = 0.0
		if C[2] < 0:
			print("Fitted parabola is concave down. Minimum alpha value is not bounded.")
			alphaStar = 1
		else:
			(alphaStar,bestY) = minimizeParabola(C)
		# Move to position of calculated alpha
		newPosition = []
		for oldPosition, slope in zip(position,slopeList):
			newPosition.append(oldPosition-slope*damping*alphaStar)
		lastPosition = position
		position = newPosition
		objectiveValueLast = objectiveValue

		if method == 'ExteriorPenalty':
			objectiveValue = evaluateExteriorPenalty(expression,
				inequalityConstraints=inequalityConstraints,
				equalityConstraints=equalityConstraints,
				variables = variables,
				values = position,
				rp = rp)
		elif method == 'InteriorLinearExtended':
			objectiveValue = evaluateLinearExtendedPenalty(expression,
				inequalityConstraints=inequalityConstraints,
				equalityConstraints=equalityConstraints,
				variables = variables,
				values = position,
				rp = rp,
				epsilon = -9999,
				evaluate=True)
		elif method == 'InteriorInverseBarrier':
			objectiveValue = evaluateInteriorInverseBarrier(expression,
				inequalityConstraints=inequalityConstraints,
				equalityConstraints=equalityConstraints,
				variables = variables,
				values = position,
				rp = rp)
		else:
			print('The method ' + method + ' is not implemented yet.')

		# Print current iteration results
		if echo == True:
			resultsString = "%i        \t" %(i)
			for value in position:
				resultsString += "%2.4f\t" % (value)
			resultsString += "{}\t".format(slopeList)
			resultsString += "%2.6f" % (objectiveValue)
			print(resultsString)

		# Check convergence
		deltaObjective = objectiveValueLast - objectiveValue
		#print("Delta Objective = %2.4f" % (float(deltaObjective)))
		if abs(float(deltaObjective)) < epsilon and i > 1:
			shouldContinue = False
			if printResults == True:
				print("Local Optimium found")

		#print("About to check iteration maximum")
		if i > nMax:
			if printResults == True:
				print("Function timed out. Returning final result")
			shouldContinue = False

	if printResults==True:
		print("#### - Results - ####")
		for variable, variableValue in zip(variables,position):
			print(str(variable) + " = %2.6f" % (variableValue))
		print("F = %2.6f" % (objectiveValue))
	return (objectiveValue, position)

def minimizeCubic(c):
	# Inputs: Coefficients for polynomial equation according to the form C0 + C1*x + C2*x^2 + C3*x^3
	# Outputs: Values of x and y where y is minimized
	a = 3*c[3]
	b = 2*c[2]
	d = c[1]
	insideSqareroot = np.float64(b*b-4*a*d)
	if insideSqareroot < 0:
		print("Minimize Cubic function encountered imaginary square root. Aborting.")
		return
	x1 = (-b+np.sqrt(insideSqareroot))/(2*a)
	x2 = (-b-np.sqrt(insideSqareroot))/(2*a)

	x = 0
	y = 0

	y1 = approx.getValueOfPoly(c,x1)
	y2 = approx.getValueOfPoly(c,x2)
	if y1 < y2:
		x = x1
		y = y1
	elif y1 > y2:
		x = x2
		y = y1
	else:
		x = x1
		y = y1
		print("More than one solution in Minimize Cubic")

	return (x,y)

def minimizeParabola(c):
	# Inputs: Coefficients for polynomial equation according to the form C0 + C1*x + C2*x^2...
	# Outputs: Values of x and y where y is minimized
	minX = -c[1]/(2*c[2])

	minY = approx.getValueOfPoly(c,minX)
	return (minX,minY)

def convertToPenaltyFunction(coreFunction,constraints,R=1):
	constraintsToSum = []
	newObjective = coreFunction + " - %2.4f*(" % (R)
	for i in range(0,len(constraints)):
		constraint = constraints[i]
		if i == 0:
			newObjective = newObjective + "1/(" + constraint + ")"
		else:
			newObjective = newObjective + " + 1/(" + constraint + ")"

	newObjective = newObjective + ")"
	return newObjective

def goldenSectionSearch(expression,xlow,xu,epsilon = 0.001,n=100,echo=False):
	tau = 0.381966

	if isinstance(expression, str):
		expression = sy.sympify(expression)

	fu = evaluateExpression(expression,variables = ['x'], values = [xu])
	flow = evaluateExpression(expression,variables = ['x'], values = [xlow])

	x1 = (1-tau)*xlow + tau*xu
	f1 = evaluateExpression(expression,variables = ['x'], values = [x1])
	x2 = tau*xlow + (1-tau)*xu
	f2 = evaluateExpression(expression,variables = ['x'], values = [x2])

	k = 3
	shouldContinue = True

	while shouldContinue == True:

		if f1 > f2:
			xlow = x1
			flow = f1
			x1 = x2
			f1 = f2
			x2 = tau*xlow + (1-tau)*xu
			f2 = evaluateExpression(expression,variables = ['x'], values = [x2])
		else:
			xu = x2
			fu = f2
			x2 = x1
			f2 = f1
			x1 = (1-tau)*xlow + tau*xu
			f1 = evaluateExpression(expression,variables = ['x'], values = [x1])

		k = k + 1
		if echo == True:
			print("i = %i \t xLow = %2.4f \t F(xLow) = %2.4f \t xHigh = %2.4f \t F(xHigh) = %2.4f" % (k,xlow,flow,xu,fu))

		if k > n:
			shouldContinue = False
		if xu - xlow < epsilon:
			shouldContinue = False
	fs = [f1,f2,flow,fu]
	xs = [x1,x2,xlow,xu]
	fMin = min(fs)
	xMin = xs[fs.index(fMin)]

	return(fMin,xMin)

def randomSearch2D(objectiveFunction,xStart,yStart,constraints,tolerance=0.0001, maxIterations=100):
	objectiveExpression = sy.sympify(objectiveFunction)
	constraintExpressions = []
	for constraint in constraints:
		constraintExpressions.append(sy.sympify(constraint))

	# Variable initializations
	xBest = 999999999.9
	yBest = 999999999.9
	objectiveBest = 9999999.9999
	x = xStart
	y = yStart
	epsilon = 100
	shouldContinue = True
	i = 0
	sinceLastPrint = 0
	printInterval = 100

	# Iteration loop
	while shouldContinue == True:
		i = i+1
		sinceLastPrint = sinceLastPrint + 1
		if sinceLastPrint >= printInterval:
			print("Running Iteration %i" %(i))
			sinceLastPrint = 0

		xNew = x + random.uniform(-0.1, 0.1)
		yNew = y + random.uniform(-0.1,0.1)
		objectiveNew = evaluateExpression(objectiveExpression,x=xNew,y=yNew)
		validPoint = True
		if objectiveNew < objectiveBest:
			for g in constraintExpressions:
				if evaluateExpression(expression=g,x=x,y=y) > 0:
					validPoint = False

			if validPoint == True:
				# Move and store new location
				xLast = x
				yLast = y
				objectiveLast = objectiveBest

				x = xNew
				y = yNew
				objectiveBest = objectiveNew

				# Check convergence
				epsilon =  objectiveLast - objectiveBest
				print("Best solution so far: %2.4f" % (objectiveBest))

		if epsilon <= tolerance or i >= maxIterations:
			shouldContinue = False

	return (x, y, objectiveBest)

def bruteForceMinimum2D(objectiveFunction,xSearchRange,ySearchRange,constraints,resolution):

	objectiveExpression = sy.sympify(objectiveFunction)
	constraintExpressions = []
	for constraint in constraints:
		constraintExpressions.append(sy.sympify(constraint))

	xArray = np.arange(xSearchRange[0], xSearchRange[1], resolution).tolist()
	yArray = np.arange(ySearchRange[0], ySearchRange[1], resolution).tolist()

	xBest = 999999999.9
	yBest = 999999999.9
	zBest = 9999999.9999

	# Iteration loop
	for x in xArray:
		print("x position: %2.4f" % (x))
		for y in yArray:
			z = evaluateExpression(expression=objectiveExpression, x=x,y=y)
			validPoint = True
			if z < zBest:
				for g in constraintExpressions:
					if evaluateExpression(expression=g,x=x,y=y) > 0:
						validPoint = False

				if validPoint == True:
					xBest = x
					yBest = y
					zBest = z

	return (xBest, yBest, zBest)

import numpy,import scipy,import sympy,from sympy


import numpy as np
import matplotlib.pyplot as plt
from newton_raphson import *
from random import uniform
import numpy.polynomial.legendre as L

###question 0

def E(X):
    ##Return the value of E(x1,...,xn)
    E=0
    N=len(X)
    for i in range(N):
        E+=np.log(abs(X[i]+1))+np.log(abs(X[i]-1))
        for j in range(N):
            if j!=i:
                if X[i]!=X[j]:
                    E+=np.log(abs(X[i]-X[j]))
                else:
                    return -7 ## To avoid a divsion by 0
    return E

def grad_E(X):
    ## Return the vector grad E as it is described in the subject
    n, m = np.shape(X)
    assert (m == 1)
    res = np.zeros([n,1])
    for i in range(n):
        s = 0
        for j in range(n):
            if (i != j):
                s += 1. / (X[i, 0] - X[j, 0])
        res[i, 0] = 1. / (1 + X[i]) + 1. / (X[i] - 1) + s
    return res

def F(X):
    return grad_E(X)

###quest1

def Jacobian_E(Y):
    ## Return the Jacobian matrix of delta_E
    N = np.shape(Y)[0]
    J=np.zeros((N,N))
    for j in range(N):
        for i in range(N):
            if i==j:
                J[j][i]=-1/(Y[i]+1)**2 - 1/(Y[i]-1)**2
                for k in range(N):
                    if k!=j:
                        J[j][i]+=-1/(Y[i]-Y[k])**2
            else:
                J[i][j]=1/(Y[i]-Y[j])**2
    return J

def J(X):
    return Jacobian_E(X)

## Two global lists to the display of the curve
norme_F=[]
iterations=[]

def Newton_Raphson_curve(f, J, U0, N, eps):
    ## The Newton-Raphson algorithm modified to allow the display of the curve ||F(X)||
    global norme_F
    global iterations
    norme_F=[]
    iterations=[]
    """
    Solve nonlinear system F=0 by Newton-Raphson's method.
    J is the Jacobian of F. At input, U0 is the starting
    position of the algorithm. The iteration continues
    until ||F|| < eps or until N iterations are reached.
    """
    F_value = f(U0)
    U = U0
    F_norm = np.linalg.norm(F_value, ord=2)
    iteration_counter = 0
    norme_F.append(F_norm)
    iterations.append(iteration_counter)
    while abs(F_norm) > eps and iteration_counter < N:
        V = np.linalg.solve(J(U), -F_value)
        U = U + V
        F_value = f(U)
        F_norm = np.linalg.norm(F_value, ord=2)
        iteration_counter += 1

        norme_F.append(F_norm)
        iterations.append(iteration_counter)

    # Here, either a solution is found, or too many iterations
    if abs(F_norm) > eps:
        iteration_counter = -1

    return U, iteration_counter

def Newton_Raphson_backtracking_curve(f, J, U0, N, eps, alpha):
    ## The Newton-Raphson algorithm with backtracking modified to allow the display of the curve ||F(X)||
    global norme_F
    global iterations
    norme_F=[]
    iterations=[]
    """
    Solve nonlinear system F=0 by Newton-Raphson's method.
    J is the Jacobian of F. At input, U0 is the starting
    position of the algorithm. The iteration continues
    until ||F|| < eps or until N iterations are reached.
    There is a backtracking to reach the solution faster.
    """
    F_value, U = f(U0), U0
    F_norm = np.linalg.norm(F_value, ord=2)
    iteration_counter = 0

    while abs(F_norm) > eps and iteration_counter < N:
        V = np.linalg.lstsq(J(U), -F_value, rcond=None)[0]
        nxt_F_norm = np.linalg.norm(f(U + V), ord=2)
        i = 0
        while nxt_F_norm >= F_norm :
            print('BACKTRACKING')
            i += 1
            nxt_F_norm = np.linalg.norm(f(U + alpha ** i * V), ord=2)

        U = U + alpha ** i * V
        F_value = f(U)
        F_norm = np.linalg.norm(F_value, ord=2)
        iteration_counter += 1

        norme_F.append(F_norm)
        iterations.append(iteration_counter)

    # Here, either a solution is found, or too many iterations
    if abs(F_norm) > eps:
        iteration_counter = -1
    return U, iteration_counter

## Initialisation of  Ten chosen charges
U0=np.zeros((10,1))
U0[0]=-0.86684278
U0[1]=0.86088026
U0[2]=0.80889216
U0[3]=-0.98098176
U0[4]=0.68707341
U0[5]=0.27329905
U0[6]=-0.07208807
U0[7]=0.6864963
U0[8]=-0.11970087
U0[9]=-0.1899953

#we could have imported the function from  newton_raphson.py but this version allows to draw the curve

def Newton_Raphson_with_backtracking(f, J, U0, N, epsilon):
    global norme_F
    global iterations
    norme_F=[]
    iterations=[]
    F_value, U = f(U0), U0
    F_norm = np.linalg.norm(F_value, ord=2)
    iteration_counter = 0
    for i in range(N):
        fu = f(U)
        na = np.linalg.norm(fu)
        if (na < epsilon):
            return U
        ju = J(U)
        V = np.linalg.lstsq(ju,-fu)[0]
        if (np.linalg.norm(f(U+V)) - na >= 0):
            V = (1.0/3.0)*V
        U = U + V
        F_value = f(U)
        F_norm = np.linalg.norm(F_value, ord=2)
        iteration_counter += 1

        norme_F.append(F_norm)
        iterations.append(iteration_counter)
    return U, iteration_counter

U,iteration_counter=Newton_Raphson_curve(F, J, U0, N=100, eps=1e-8)

def test_equilibrium():
    #a function that shows the electrostatic equilibrium with 10 charges with and without backtracking
    U,iteration_counter=Newton_Raphson_curve(F, J, U0, N=100, eps=1e-8)
    print("Test with 10 charges, initialisation of U0 : ",U0.transpose())
    print("Final positions of the charges : ",U.transpose())
    plt.plot(iterations,norme_F,label="without backtracking")
    plt.title("Electrostatic equilibrium with 10 charges")
    Newton_Raphson_with_backtracking(F, J, U0, N=100, epsilon=1e-8)
    #plt.plot(iterations,norme_F,label="using backtracking")
    plt.xlabel("Number of iterations")
    plt.ylabel("||F(X)||")
    plt.title("Electrostatic equilibrium with 10 charges with and without backtracking")
    plt.semilogy()
    plt.legend()
    plt.show()

def position_real_axis():
    #a function that shows the final position ofthe charges on the real axis
    plt.figure(num=2,figsize=(7,1.5))
    print("Final positions on the real axis")
    plt.title("Position of the charges")
    plt.xlabel("x axis")
    plt.plot([min(U),max(U)],[0,0],color="red",label="Real axis")
    plt.plot(U,[0]*len(U),'o',color="yellow",label="Charge")
    plt.legend()
    plt.show()

def energy_one_charge_position():
    ## The plot of the curve for one charge
    size=50

    O=np.linspace(-0.99,0.99,size)
    V=np.zeros((size,1))
    for k in range(size):
       V[k]=E([O[k]])

    print("Graph describing the evolution the electrostatic energy of one charge")
    plt.title("Electrostatic energy of one charge")
    plt.ylabel("Energy")
    plt.xlabel("Position of the charge")
    plt.plot(O,V)
    plt.show()

def mirror(A):
    n = A.size
    for i in range(n//2):
        tmp = A[i]
        A[i] = A[n-i-1]
        A[n-i-1] = tmp
    return A

#Plot Legendre polynomials and equilibrium positions
def add_plot(X, lbl, clr, type='o'):
    Peq = Newton_Raphson(grad_E, Jacobian_E, X, 100, 1e-8)
    n= Peq.size
    for i in range(n):
        plt.plot(Peq[i,0],0,type, color=clr)

    c = [0]*(n+2)
    c[n+1] = 1

    d = L.legder(c)
    P = L.leg2poly(d)

    P = mirror(P)
    Poly = np.poly1d(P)
    x = np.linspace(-1,1,100)
    y = Poly(x)
    plt.plot(x, y, label=lbl, color=clr)

#Electrostatic Equilibrium Test

def elec_equ_test():

    A = np.matrix([[0.2]])

    B = np.matrix([[0.5],
                   [0.6]])

    C = np.matrix([[0.4],
                   [-0.5],
                   [0.7]])

    D = np.matrix([[0.4],
                   [-0.4],
                   [0.5],
                   [0.6]])
    add_plot(A, 'n=1', 'r')
    add_plot(B, 'n=2', 'y')
    add_plot(C, 'n=3', 'b')
    add_plot(D, 'n=4', 'g')
    plt.plot([-1,1], [0,0], 'k')
    plt.axis([-1,1,-4,4])
    plt.xlabel("x")
    plt.ylabel("y")
    plt.legend()
    plt.title("Legendre polynomials and equilibrium positions")
    plt.show()

if __name__ == '__main__':
    test_equilibrium()
    position_real_axis()
    energy_one_charge_position()
    elec_equ_test()


import numpy as np

def least_squares(ds, t, n = 2):

    # To generalize it for higher dimensions, but not necessary
    T = np.zeros((ds.shape[0], n + 1))

    # Add each iteration of t^n
    for idx in range(n + 1):
        T[:,idx] = t**idx

    # As defined in Theory.
    A = T.T @ T
    b = T.T @ ds

    return np.linalg.solve(A, b)

def chi2(actual, predicted, std):
    # The difference of the actual and predicted data
    difference = actual - predicted

    # Accounting for standard deviation
    division = difference/std

    # Returning the value of chi2
    return np.sum(division**2)

def rmse(actual, predicted, std):
    # chi2.
    goodness_of_fit = chi2(actual, predicted, std)

    # Returning the value of rmse.
    return np.sqrt(goodness_of_fit/actual.shape[0])


import numpy as np
import math

#Function to calculate cosine between two vectors. Returns any value in [-1,1]
#Result of cos(K) can be +1 for K=0 degrees (same angle->similar vectors)
#  0 for K=90 degrees (orthogonal angle -> not similar vectors)
# -1 for K=180 degrees (diametrical angle -> opposite vectors)
def cosine_similarity(vector1, vector2):
	xx = 0
	xy = 0
	yy = 0
	for i in range(len(vector1)):
		x = vector1[i]
		y = vector2[i]
		xx += (x*x)
		xy += (x*y)
		yy += (y*y)
	return (xy/math.sqrt(xx*yy))

#Function to convert every item of a column into an integer
#Returns NaN for non convertible items
def convert_int(xcolumn):
	try:
		return int(xcolumn)
	except:
		return np.nan

#calculate ratings by using weighted vectors
def calculate_rating(candidate, mr, mean_ratings):
	v = candidate['vote_count']
	u = candidate['vote_average']
	return (v/(v+mr)*u) + (mr/(mr+v)*mean_ratings)

#function takes in a list of tags and filters the ones that exist within list s
def filter_tags(taglist, s):
	tag = []
	for i in taglist:
		if i in s:
			tag.append(i)
	return tag

#function takes in two lists of predictions vs actual ratings and returns a mean error using RMSE formula
def eval_RMSE(pred_x, actu_y):
	y_actual = np.array(actu_y)
	y_predicted = np.array(pred_x)
	error =(y_actual-y_predicted)**2
	error_mean=float(np.mean(error))
	error_squared=float(math.sqrt(error_mean))
	return error_squared


import numpy as np
from scipy.stats import multivariate_normal as mvn

def isposdef(A):
    try:
        _ = np.linalg.cholesky(A)
        return True
    except:
        return False

# test
np.random.seed(42)
D = 5
A = np.random.randn(D, D)
assert not(isposdef(A))
assert isposdef(np.dot(A, A.T))

def sample_mvn(mu, Sigma, N):
    L = np.linalg.cholesky(Sigma)
    D = len(mu)
    Z = np.random.randn(N, D)
    X = np.dot(Z, L.T) + np.reshape(mu, (-1,D))
    return X

# test
D = 5
np.random.seed(42)
mu = np.random.randn(D)
A = np.random.randn(D,D)
Sigma = np.dot(A, A.T)
N = 1000
X = sample_mvn(mu, Sigma, N)
C = np.cov(X, rowvar=False)
assert np.allclose(C, Sigma, 1e-0)

dist = mvn(mu, Sigma)
N = 1000
X = dist.rvs(size=N)
C = np.cov(X, rowvar=False)
assert np.allclose(C, Sigma, 1e-0)


#! /usr/bin/env python

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

def normal_cdf(x: float, mu: float = 0, sigma: float = 1) -> float:
    return (1 + math.erf((x - mu) / (math.sqrt(2) * sigma))) / 2

# def normal_cdf_plots(xs, mu, sigma, color):
#     axes.plot(xs, [normal_cdf(x, mu, sigma) for x in xs], color=color,
#               label=f'mu = {mu}, sigma = {sigma}')
#     axes.set_title('Various normal CDFs')
#     axes.legend(loc=4)
#     fig.tight_layout()
#     plt.grid(True, linestyle='--')

# # parameter values
# mu_list = [0, 0, 0, -1]
# sigma_list = [1, 2, 0.5, 1]
# colors = ['dodgerblue', 'forestgreen', 'firebrick', 'darkorchid']

# # plots
# xs = np.linspace(-6, 6, 100)
# fig, axes = plt.subplots(1, 1, figsize=(8, 6))
# for mu, sg, cl in zip(mu_list, sigma_list, colors):
#     normal_cdf_plots(xs, mu, sg, cl)
# plt.show()

def inverse_normal_cdf(p: float, mu: float = 0, sigma: float = 1, tol: float = 1e-5) -> float:
    if mu != 0 or sigma != 1:
        return mu + sigma * inverse_normal_cdf(p, tol=tol)
    low_z = -10.0
    hi_z = 10.0
    while hi_z - low_z > tol:
        mid_z = (hi_z + low_z) / 2
        mid_p = normal_cdf(mid_z)
        if mid_p < p:
            low_z = mid_z
        else:
            hi_z = mid_z
    return mid_z

if __name__ == "__main__":
    p = float(input('Enter a value for p: '))
    # mu = float(input('Enter a value for mu: '))
    # sigma = float(input('Enter a value for sigma: '))
    print(f'The inverse is: {inverse_normal_cdf(p, mu=0, sigma=1)}')


from math import acos, atan2, cos, pi, sin
from numpy import array, cross, dot, float64, hypot, zeros

#### Print Utils #####

tol = 1e-4

def print_separator():
    print("\n")
    for i in range(2):
        print("#"* 50)
    print("\n")

def R_to_axis_angle(matrix):
    """Convert the rotation matrix into the axis-angle notation.
    Conversion equations
    ====================
    From Wikipedia (http://en.wikipedia.org/wiki/Rotation_matrix), the conversion is given by::
        x = Qzy-Qyz
        y = Qxz-Qzx
        z = Qyx-Qxy
        r = hypot(x,hypot(y,z))
        t = Qxx+Qyy+Qzz
        theta = atan2(r,t-1)
    @param matrix:  The 3x3 rotation matrix to update.
    @type matrix:   3x3 numpy array
    @return:    The 3D rotation axis and angle.
    @rtype:     numpy 3D rank-1 array, float

    From https://github.com/Wallacoloo/printipi/blob/master/util/rotation_matrix.py#L178
    Copyright (C) Edward d'Auvergne
    """

    # Axes.
    axis = zeros(3, float64)
    axis[0] = matrix[2,1] - matrix[1,2]
    axis[1] = matrix[0,2] - matrix[2,0]
    axis[2] = matrix[1,0] - matrix[0,1]

    # Angle.
    r = hypot(axis[0], hypot(axis[1], axis[2]))
    t = matrix[0,0] + matrix[1,1] + matrix[2,2]
    theta = atan2(r, t-1)

    # Normalise the axis.
    axis = axis / r

    # Return the data.
    return axis, theta


import numpy as np
A = np.array([1, 0, 0])
B = np.array([0, 1, 0])

# Are A and B orthogonal? The dot product should be zero.

dot_product = np.dot(A, B)

assert dot_product == 0

# Then we can use the transpose
assert np.matmul(A.T, B) == 0

# These vectors are the x and y plane. But how do we get a vector that is normal to the xy -plane?
# We use the crossproduct of the vectors.

cross_product = np.cross(A, B)

# It is the z-axis.
#[0, 0, 1]

assert np.allclose(cross_product, [0, 0, 1])


'''
autograd package of torch allows us to compute gradients automatically
'''
## Here we will do everything from scratch
'''
import numpy as np

x=np.array([1,2,3,4], dtype=np.float32)
y=np.array([2,4,6,8], dtype=np.float32)

w=0.0

## Model Prediction
def forward(x):
    return w*x

## Loss
def loss(y, y_pred):
    return ((y_pred-y)**2).mean()

## gradient
# MSE = 1/N * (w*x - y)**2
#  dLoss/dw = 1/N * 2x(w*x - y)
def gradient(x,y,y_pred):
    return np.dot(2*x, y_pred - y).mean()

print(f'Prediction before training: f(5) = {forward(5)}')

## Training
learning_rate = 0.01
n_iters = 15
for epoch in range(n_iters):
    # prediction = forward pass
    y_pred = forward(x)
    # loss
    l=loss(y,y_pred)
    #gradients
    dw = gradient(x,y,y_pred)
    # update
    w = w-learning_rate*dw
    print(f"epoch{epoch+1}: w = {w:.3f}, loss:{l:.8f}")

print(f"Prediction after training: f(5) = {forward(5):.3f}")
'''
## Pytorch Implementation
import torch

x = torch.tensor([1,2,3,4], dtype=torch.float32)
y = torch.tensor([2,4,6,8], dtype=torch.float32)

w=torch.tensor(0.0, dtype=torch.float32, requires_grad=True)

## Model Prediction
def forward(x):
    return w*x

## Loss
def loss(y, y_pred):
    return ((y_pred-y)**2).mean()
y_pred = forward(x)
l=loss(y,y_pred)
learning_rate = 0.01
## gradient
l.backward() # dLoss/dw

#update weights
with torch.no_grad():
    w-=learning_rate*w.grad
w.grad.zero_()
print(f'Prediction before training: f(5) = {forward(5)}')


import numpy as np
import matplotlib.pyplot as plt

N = 200
x_start, x_end = -4.0, 4.0
y_start, y_end = -2.0, 2.0

x = np.linspace(x_start, x_end, N)
y = np.linspace(y_start, y_end, N)

X, Y = np.meshgrid(x, y)

u_inf = 1.0

u_freestream = u_inf * np.ones((N, N), dtype=float)
v_freestream = np.zeros((N, N), dtype=float)

psi_freestream = u_inf * Y

def get_velocity(strength, xs, ys, X, Y):
    u = strength / (2 * np.pi) * (X - xs) / ((X - xs)**2 + (Y - ys)**2)
    v = strength / (2 * np.pi) * (Y - ys) / ((X - xs)**2 + (Y - ys)**2)

    return u, v

def get_stream_function(strength, xs, ys, X, Y):
    psi = strength / (2 * np.pi) * np.arctan2((Y - ys), (X - xs))

    return psi

strength_source = 5.0
x_source, y_source = -1.0, 0.0

u_source, v_source = get_velocity(strength_source, x_source, y_source, X, Y)

psi_source = get_stream_function(strength_source, x_source, y_source, X, Y)

# Superposition of the source on the freestream
u = u_freestream + u_source
v = v_freestream + v_source
psi = psi_freestream + psi_source

size = 10
plt.figure(figsize=(size, (y_end - y_start) / (x_end - x_start) * size))
plt.grid(True)
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.xlim(x_start, x_end)
plt.ylim(y_start, y_end)
plt.streamplot(X, Y, u, v, density=2, linewidth=1,
               arrowsize=1, arrowstyle='->')
plt.scatter(x_source, y_source, color='#CD2305', s=80, marker='o')

x_stagnation = x_source - strength_source / (2 * np.pi * u_inf)
y_stagnation = y_source

plt.scatter(x_stagnation, y_stagnation, color='g', s=80, marker='o')

plt.contour(X, Y, psi,
            levels=[-strength_source / 2, +strength_source / 2],
            colors='#CD2305', linewidths=2, linestyles='solid')

plt.show()

# Source-sink pair in a uniform flow
strength_sink = -5.0
x_sink, y_sink = 1.0, 0.0

u_sink, v_sink = get_velocity(strength_sink, x_sink, y_sink, X, Y)

psi_sink = get_stream_function(strength_sink, x_sink, y_sink, X, Y)

u = u_freestream + u_source + u_sink
v = v_freestream + v_source + v_sink
psi = psi_freestream + psi_source + psi_sink

size = 10
plt.figure(figsize=(size, (y_end - y_start) / (x_end - x_start) * size))
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.xlim(x_start, x_end)
plt.ylim(y_start, y_end)
plt.streamplot(X, Y, u, v, density=2, linewidth=1,
               arrowsize=1, arrowstyle='->')
plt.scatter([x_source, x_sink], [y_source, y_sink],
            color='#CD2305', s=80, marker='o')
plt.contour(X, Y, psi, levels=[0.], colors='#CD2305',
            linewidths=2, linestyles='solid')

plt.show()

# Computes the pressure coefficient field
cp = 1.0 - (u**2 + v**2) / u_inf**2

# Plots the pressure coefficient field
size = 10
plt.figure(figsize=(1.1 * size, (y_end - y_start) / (x_end - x_start) * size))
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.xlim(x_start, x_end)
plt.ylim(y_start, y_end)
contf = plt.contourf(
    X, Y, cp, levels=np.linspace(-2.0, 1.0, 100),
    extendf='both', cmap=plt.cm.coolwarm)
cbar = plt.colorbar(contf)
cbar.set_label('$C_p$', fontsize=16)
cbar.set_ticks([-2.0, -1.0, 0.0, 1.0])
plt.scatter([x_source, x_sink], [y_source, y_sink],
            color='#CD2305', s=80, marker='o')
plt.contour(X, Y, psi, levels=[0.], colors='#CD2305',
            linewidths=2, linestyles='solid')
cbar.ax.invert_yaxis()  # Real aerodynamicist...

plt.show()


import sympy
from prettytable import PrettyTable
import totalPivotingMethod
x = sympy.Symbol('x')
table = PrettyTable()

xn =[1,3,4,5,7]
fxn =[4.31,1.5,3.2,2.6,1.8]

inequality =[]
functions =[]
result =[]
des = []
'''
y-y1 = m(x-x1)

y = y1+((y1-y0)/(x1-x0))*(x-x1)
'''

def createInequality():
    for i in range(0,len(xn)-1):
        if(i < len(xn)):
            inequality.append(((xn[i],fxn[i]),(xn[i+1],fxn[i+1])))

def linear():

    for i in inequality:
        print(i)
        y = i[1][1]+((i[1][1]-i[0][1])/(i[1][0]-i[0][0])*(x-i[1][0]))
        result.append(y)
        des.append(str(i[0][0]) + " <= x <= " +str(i[1][0]))
    table.add_column("equations",result)
    table.add_column("inequality",des)
    print(table)

createInequality()

linear()


import numpy as np
from showml.losses import MeanSquareError, BinaryCrossEntropy

def r2_score(y: np.ndarray, z: np.ndarray) -> float:
    """
    Calculate the r^2 (coefficient of determination) score of the model
    param y: The true values
    param z: The predicted values
    return: The r^2 score
    """
    rss = np.sum(np.square(y - z))
    tss = np.sum(np.square(y - np.mean(y)))
    r_2 = 1 - (rss / tss)
    return r_2

def accuracy(y: np.ndarray, z: np.ndarray, logits: bool = True) -> float:
    """
    Compute the classification accuracy of the model
    param y: The true labels
    param z: The predicted labels
    param logits: A flag indicating that the predicted values are probabilites and not classes
    """
    if logits:
        predicted_classes = [1 if i > 0.5 else 0 for i in z]
    return np.sum(y == predicted_classes) / len(y)

def mean_square_error(y: np.ndarray, z: np.ndarray) -> float:
    return MeanSquareError().objective(y, z)

def binary_cross_entropy(y: np.ndarray, z: np.ndarray) -> float:
    return BinaryCrossEntropy().objective(y, z)


import numpy.random as npr
from pylab import*
from random import*
from math import*
import numpy as np

#Application1: Methode de Box-Muller

def densitef(x):
    return (1/sqrt(2*pi))*exp(-pow(x,2)/2)

def LoiNormale():
    U1=random()
    U2=random()
    X=sqrt(-2*log(U1))*cos(2*pi*U2)
    return X

def LoiNormaleN(N):
    Y=np.zeros(N)
    for i in range(N):
        Y[i]=LoiNormale()
    return Y

print(LoiNormaleN(10))

#Histogramme
import scipy
from pylab import*
import pylab as pylab
from pylab import*

tableau=LoiNormaleN(10000)
m1=pylab.hist(tableau,bins=linspace(-10,10,21),normed=True)
x=linspace(-10,10,21)
y=linspace(-10,10,21)
for i in range (21):
         y[i]=densitef(x[i])
plot(x,y,color='red')

#Application2: Methode de Rejet via la loi de Laplace

def densiteg(x):
    return (1/2)*exp(-np.abs(x))

def  LoideLaplace():
    U=random()
    X=-sign(U-0.5)*log(1-2*np.abs(U-0.5))
    return X

def LoideLaplaceN(N):
    Y=np.zeros(N)
    for i in range(N):
        Y[i]=LoideLaplace()
    return Y

print(LoideLaplaceN(5))

#Simulation d'une loi normale a l'aide d'une loi de Laplace

def LoiNormalefromLoiLaplace():
    teta=np.sqrt(2*(np.e)/np.pi)
    y= LoideLaplace()
    uk = npr.random()
    while densitef(y)<=uk*teta*densiteg(y):
            y= LoideLaplace()
    return y

def LoiNormalefromLoiLaplaceN(N):
   x=np.zeros(N)

   for k in range(N):
       x[k]= LoiNormalefromLoiLaplace()
   return x

print(LoiNormalefromLoiLaplaceN(10))

tableau=LoiNormalefromLoiLaplaceN(10000)
m1=pylab.hist(tableau,bins=linspace(-10,10,21),normed=True)
x=linspace(-10,10,21)
y=linspace(-10,10,21)
for i in range (21):
         y[i]=densitef(x[i])
plot(x,y,color='red')

#Application3: Lois derives de la loi Normale

def I(x):
    if x<0:
        return 0
    else:
        return 1

def densitekhi(x,n):
    return pow(x,(n/2)-1)*exp(-x/2)*I(x)/((pow(2,n/2))*math.gamma(n/2))

def S(n):
    somme=0
    for i in range(n):
        somme=somme+ pow(LoiNormale(),2)
    return somme

def LoiKhideux(n,N):

    Y=np.zeros(N)
    for i in range(N):
        Y[i]=S(n)
    return Y
#Histogramme de la loi Khideux
n=5
tableau=LoiKhideux(n,10000)
m1=pylab.hist(tableau,bins=linspace(-10,10,21),normed=True)
x=linspace(-10,10,21)
y=linspace(-10,10,21)
for i in range (21):
         y[i]=densitekhi(x[i],n)
plot(x,y,color='red')

#Application4: Loi de Student

def densiteStudent(x,n):
    return math.gamma((n+1)/2)/(math.gamma(n/2)*sqrt(pi*n)*pow(1+pow(x,2)/n,(n+1)/2))

def X(n):
    Z=LoiNormale()
    Yn=S(n)
    return Z/(sqrt(Yn/n))

def LoideStudent(n,N):

    Y=np.zeros(N)
    for i in range(N):
        Y[i]=X(n)
    return Y
#Histogramme de la loi Student
n=5
tableau=LoideStudent(n,10000)
m1=pylab.hist(tableau,bins=linspace(-10,10,21),normed=True)
x=linspace(-10,10,21)
y=linspace(-10,10,21)
for i in range (21):
         y[i]=densiteStudent(x[i],n)
plot(x,y,color='red')

#RESULTAT REMARQUABLE
def LoiNormale1(m,l2):
    X=LoiNormale()
    return sqrt(l2)*X+m

def Xnmoy(m,n,l2):

    somme=0
    for i in range(n):
        somme=somme+LoiNormale1(m,l2)
    return somme/n

def LoiSn2(m,n,l2):
    somme=0
    for i in range(n):
        somme=pow((LoiNormale()-Xnmoy(m,n,l2)),2)
    return somme/(n-1)

def Zn(m,n,l2):
    return ((n-1)*LoiSn2(m,n,l2))/l2

def Tn(m,n,l2):
    return sqrt(n)*(Xnmoy(m,n,l2)-m)/LoiSn2(m,n,l2)

def LoiZn(m,n,l2,N):
    Y=np.zeros(N)
    for i in range(N):
        Y[i]=Zn(m,n,l2)
    return Y

def LoiTn(m,n,l2,N):
    T=np.zeros(N)
    for i in range(N):
        T[i]=Tn(m,n,l2)
    return T

#Simulation experimentale----Voir histogrammes

#Pour Zn
m=3
l2=2
n=6
tableau=LoiZn(m,n,l2,10000)
m1=pylab.hist(tableau,bins=linspace(-20,20,41),normed=True)
x=linspace(-20,20,41)
y=linspace(-20,20,41)
for i in range (41):
         y[i]=densitekhi(x[i],n)
plot(x,y,color='red')

#Pour Tn
m=3
l2=2
n=6
tableau=LoiTn(m,n,l2,10000)
m1=pylab.hist(tableau,bins=linspace(-20,20,41),normed=True)
x=linspace(-20,20,41)
y=linspace(-20,20,41)
for i in range (41):
         y[i]=densiteStudent(x[i],n-1)
plot(x,y,color='red')

#Lien avec la loi binomiale
def Binomiale(n,p):
    X=0
    for i in range(n):
        if (random()<=p):
            X+=1
    return X

def NBinomialeloi(N,n,p):

    x=np.zeros(N)
    for k in range(N):
      x[k]=Binomiale(n,p)
    return x

def Bn(n,p):
    Y=Binomiale(n,p)-n*p
    return Y/(sqrt(n*p*(1-p)))

def LoiBn(n,p,N):

    B=np.zeros(N)
    for i in range(N):
        B[i]=Bn(n,p)

    return B

#Pour Bn
p=0.5
n=5
tableau=LoiBn(n,p,10000)
m1=pylab.hist(tableau,bins=linspace(-20,20,41),normed=True)
x=linspace(-20,20,41)
y=linspace(-20,20,41)
for i in range (41):
         y[i]=densitef(x[i])
plot(x,y,color='red')

#Lien avec la loi de Poisson

def LoiExponentielle(a):
    U=random()
    return -(1./a)*(log(1-U))

def LoiExponentielleN(a,n):
    vecteur=np.zeros(n)
    for i in range(n):
        vecteur[i]=LoiExponentielle(a)
    return vecteur

def Sn1(T,n):
    somme1=0
    for i in range(n):
        somme1=somme1+T[i]
    return somme1

def LoidePoisson(a):
    n=100
    vecteur=LoiExponentielleN(a,n)
    somme1=0
    for i in range (n):
        if Sn1(vecteur,i)<=1:
            somme1=somme1+1
    return somme1

def LoidePoissonN(a,N):
   x=np.zeros(N)
   for k in range(N):
       x[k]= LoidePoisson(a)
   return x

def densitef2p(x,m,l2):
    return 1/np.sqrt(2*pi*l2)*exp(-pow(x-m,2)/(2*l2))

#Histogramme pour la loi de poisson
a=100
N=1000
tableau=LoidePoissonN(a,N)
m1=pylab.hist(tableau,bins=linspace(0,200,201),normed=True)
x=linspace(0,200,201)
y=linspace(0,200,201)
for i in range (201):
         y[i]=densitef2p(x[i],a,a)
plot(x,y,color='red')


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

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

#计算均值,要求输入数据为numpy的矩阵格式，行表示样本数，列表示特征
def meanX(dataX):
  return np.mean(dataX, axis=0) #axis=0表示按照列来求均值，如果输入list,则axis=1

def dataAdjust(dataX):
  average = meanX(dataX)
  m, n = np.shape(dataX)
  data_adjust = []
  avgs = np.tile(average, (m,1))
  data_adjust = dataX - avgs
  return data_adjust, average

"""
参数：
 - XMat：传入的是一个numpy的矩阵格式，行表示样本数，列表示特征
 - k：表示取前k个特征值对应的特征向量
返回值：
 - finalData：参数一指的是返回的低维矩阵，对应于输入参数二
 - reconData：参数二对应的是移动坐标轴后的矩阵
"""
def pca(XMat, k):
  m, n = np.shape(XMat)
  data_adjust, average = dataAdjust(XMat)
  covX = np.cov(data_adjust.T)   #计算协方差矩阵
  featValue, featVec = np.linalg.eig(covX)
  index = np.argsort(-featValue)
  finalData = []
  if k > n:
    print "k must be less than feature number"
    return
  else:
    selectVec = np.matrix(featVec.T[index[:k]])
    finalData = data_adjust * selectVec.T
    reconData = (finalData * selectVec) + average
    return finalData, reconData

def loaddata(datafile):
  return np.array(pd.read_csv(datafile, sep="\t", header=-1)).astype(np.float)

def plotBestFit(data1, data2):
  dataArr1 = np.array(data1)
  dataArr2 = np.array(data2)

  m = np.shape(dataArr1)[0]
  axis_x1 = []
  axis_y1 = []
  axis_x2 = []
  axis_y2 = []
  for i in range(m):
    axis_x1.append(dataArr1[i, 0])
    axis_y1.append(dataArr1[i, 1])
    axis_x2.append(dataArr2[i, 0])
    axis_y2.append(dataArr2[i, 1])
  fig = plt.figure()
  ax = fig.add_subplot(111)
  ax.scatter(axis_x1, axis_y1, s=50, c='red', marker='s')
  ax.scatter(axis_x2, axis_y2, s=50, c='blue')
  plt.xlabel('x1')
  plt.ylabel('x2')
  plt.savefig("outfile.png")
  plt.show()

#根据数据集data.txt
def main():
  datafile = "../data/data.txt"
  XMat = loaddata(datafile)
  k = 2
  return pca(XMat, k)

if __name__ == "__main__":
  finalData, reconMat = main()
  plotBestFit(finalData, reconMat)


#%% direction field
import numpy as np
import matplotlib.pyplot as plt
#%%

# step
nx, ny = .3, .3

# range
x = np.arange(0, 5, nx)
y = np.arange(-5, 5, ny)
X, Y = np.meshgrid(x, y)

#%% UNCOMMENT ONE OF THESE dy's
# differential equation 1
dy = X + np.sin(Y)

# differential equation 2
# dy = -X

# differential equation 3
# dy = Y

# differential equation 4
# dy = Y*(1-Y)*X

# differential equation 5
# dy = (Y**2)*X*np.exp(-X)

# differential equation 6
# dy = (Y+X)/(Y*X)

# differential equation 7
# dy = -0.5*Y

# differential equation 8
# dy = 4*Y*X

#%%
dx = np.ones(dy.shape)
color = dy
#lw = 1
plt.figure(figsize=[10,10])
plt.streamplot(X,Y,dx, dy, color=color, density=1, cmap='jet', arrowsize=1.5)
#plt.plot(x,y)


import numpy as np

def add_form_default_values():
    """
    Change default form rendering on page load.

    Add default form values i.e. {'<variable name>': {'value': 10}}
    Ignore some of the input fields i.e. {'<variable name>': {'action': 'ignore'}}
    """
    return {'ambient_temp_deg_c': {'action': 'update', 'value': 20}}

def area(d):
    """Cross sectional area."""
    return np.pi * d**2 / 4

def surface_area(d, L):
    """Surface area in m^2"""
    return np.pi * d * 1e-3 * L

def resistance(row, L, A):
    """A= area in mm^2     L = length of wire in meters     row = resistivity in ohm.mm^2/m"""
    return row * L / A

def power(V, R):
    """Electrical power Watts.     V in volts, R in ohms"""
    return V**2 / R

def element_temp(Q, L, d, emis, ambient_deg_c):
    h = 8  # W/m2.K  heat transfer coefficient- guess
    T0 = ambient_deg_c + 273.15  # Kelvin
    A2 = surface_area(d, L)  # surface area of wire
    T_arr = np.arange(0, 10000, 10)
    Q_arr = h * A2 * (T_arr - T0) + emis * 5.67e-8 * A2 * (T_arr**4 - T0**4)
    return np.interp(Q, Q_arr, T_arr)

def validate_numeric(form_data, key):
    try:
        float(form_data[key])
    except ValueError:
        raise Warning('Numeric input required for: {}'.format(key))

def main(form_data):
    """
    The function which gets executed upon the user pressing submit button.
    Args:         form_data(dict): A dictionary of the field data. The keys to the form
                                   data dictionary are the field names.
    """

    row = form_data['wire_resistance']['resistance']
    d = form_data['wire_diameter_mm']
    emis = form_data['wire_resistance']['emissivity']
    V = form_data['voltage']
    L = form_data['wire_length_meters']
    ambient = form_data['ambient_temp_deg_c']

    for key in ['voltage', 'wire_diameter_mm', 'wire_length_meters', 'ambient_temp_deg_c']:
        validate_numeric(form_data, key)

    A = area(d)  # mm^2
    R = resistance(row, L, A)  # ohms
    Q = round(power(V, R), 5)  # Watts
    T = round(element_temp(Q, L, d, emis, ambient), 5)  # Kelvin
    return {
        'output_heading': {
            'action': 'update'
        },
        'container_outputs': {
            'action': 'update'
        },
        'wire_resistance_ohms': {
            "action": "update",
            "value": round(R, 5)
        },
        "power_watts": {
            "action": "update",
            "value": Q
        },
        "element_temp": {
            "action": "update",
            "value": T
        }
    }


from sympy import ( symbols, solve, diff, integrate, exp, sqrt, lambdify, Integral, ln, pprint, oo )

# The length (in centimeters) of a petal on a certain flower is a random variable with probability density function defined by

x = symbols( 'x' )
F = 1 / ( 4 * sqrt( x ) )

# for x in [4,16].

a, b = 4, 16

def mean( f, var, a, b ):
	return integrate( var * f, ( var, a, b ) )

def variance( f, var, a, b, mu ):
	return integrate( ( ( var **2 ) * f ), ( var, a, b ) ) - mu**2

def std_dev( var ):
	return sqrt( var )

# The expected petal length is
mu = round( mean( F, x, a, b ), 3 )

# The standard deviation is
var = round( variance( F, x, a, b, mu ), 3 )
stdev = round( sqrt( var ), 3 )

# The probability that a petal selected at random has a length less than 2 standard deviations below the mean is
prob_within_1sd = integrate( F, ( x, 4,  mu - stdev * 2 ) )
round( prob_within_1sd, 2 ) # Not in bounds

# mean
m = symbols( 'm' )
mf = Integral( F, ( x, a, m ) ).doit()
median = solve( mf - .5 , m )[ 0 ]


import numpy as np
import os
from PIL import Image
import matplotlib.pyplot as plt

def im2col(input_data, FH, FW, stride=1, pad=0):
    N, C, H, W = input_data.shape

    # first apply padding to the image
    # since image has the shape of (N,C,H,W) we apply padding to 2nd and 3rd dimensions
    img = np.pad(input_data, [(0, 0), (0, 0), (pad, pad), (pad, pad)], 'constant')

    OH = (H + 2 * pad - FH) // stride + 1
    OW = (W + 2 * pad - FW) // stride + 1

    # first with initialize col with shape (N,C,FH,FW,OH,OW)
    col = np.zeros((N, C, FH, FW, OH, OW))

    for y in range(FH):
        y_max = y + OH * stride
        for x in range(FW):
            x_max = x + OW * stride
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    # eventually we return col with shape (N*OH*OW,C*FH*FW)
    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(-1, C * FH * FW)
    return col

def col2im(col, input_shape, FH, FW, stride=1, pad=0, is_backward=True):
    N, C, H, W = input_shape
    OH = (H + 2 * pad - FH) // stride + 1
    OW = (W + 2 * pad - FW) // stride + 1

    # change col to have the shape (N,C,FH,FW,OH,OW)
    # originally col has the shape (N*OH*OW,C*FH*FW)
    col = col.reshape(N, OH, OW, C, FH, FW).transpose(0, 3, 4, 5, 1, 2)

    # we initialize img as below, stride-1 is added to prevent errors due to image shrinking after im2col
    img = np.zeros((N, C, H + 2 * pad + stride - 1, W + 2 * pad + stride - 1))

    for y in range(FH):
        y_max = y + OH * stride
        for x in range(FW):
            x_max = x + OW * stride
            if is_backward:
                img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]
            else:
                img[:, :, y:y_max:stride, x:x_max:stride] = col[:, :, y, x, :, :]
    return img[:, :, pad:H + pad, pad:W + pad]

if __name__ == '__main__':
    folder = "/Users/samrullo/datasets/images/alphabet/letters"
    img_files = os.listdir(folder)
    img_file = img_files[np.random.choice(np.arange(len(img_files)), 1)[0]]
    img = Image.open(os.path.join(folder, img_file))
    img_arr = np.array(img)

    # bring Channel to the first dimension so that img has the shape (C,H,W)
    # then reshape it to (1,C,H,W)
    img_arr = img_arr.transpose(2, 0, 1)
    C, H, W = img_arr.shape
    img_arr = img_arr.reshape(1, C, H, W)

    print(f"{img_file} has the shape : {img_arr.shape}")
    FH, FW, stride, pad = 6, 6, 2, 1
    col = im2col(img_arr, FH, FW, stride=stride, pad=pad)
    print(f"resulting col shape from im2col : {col.shape}")
    rev_img = col2im(col, img_arr.shape, FH, FW, stride, pad, is_backward=False)
    print(f"img shape after col2im : {rev_img.shape}")
    diff = (rev_img - img_arr).sum()
    print(f"diff between img and rev_img : {diff}")


import numpy as np
import matplotlib.pyplot as plt

# Plot 2D points
def displaypoints2d(points):
  plt.figure(0)
  plt.plot(points[0,:],points[1,:], '.b')
  plt.xlabel('Screen X')
  plt.ylabel('Screen Y')

# Plot 3D points
def displaypoints3d(points):
  fig = plt.figure(1)
  ax = fig.add_subplot(111, projection='3d')
  ax.scatter(points[0,:], points[1,:], points[2,:], 'b')
  ax.set_xlabel("World X")
  ax.set_ylabel("World Y")
  ax.set_zlabel("World Z")

def cart2hom(points):
  """ Transforms from cartesian to homogeneous coordinates.

  Args:
    points: a np array of points in cartesian coordinates

  Returns:
    points_hom: a np array of points in homogeneous coordinates
  """

  #
  # You code here
  #

  # points = np.transpose(points)
  new_ele = [[1]]
  points_hom = np.append(points,new_ele, axis = 0)
  return points_hom

def hom2cart(points):
  """ Transforms from homogeneous to cartesian coordinates.

  Args:
    points: a np array of points in homogenous coordinates

  Returns:
    points_hom: a np array of points in cartesian coordinates
  """

  #
  # You code here
  #
  # points = np.transpose(points)
  #hom2cart = points/points[-1]
  # Delete the last element
  points_hom = np.delete(points, -1, axis = 0)
  return points_hom

def gettranslation(v):
  """ Returns translation matrix T in homogeneous coordinates for translation by v.

  Args:
    v: 3d translation vector

  Returns:
    T: translation matrix in homogeneous coordinates
  """

  #
  # You code here
  #

  # translation matrix:
  # [[1,0,vx],
  #  [0,1,vy],
  #  [0,0,1],
  T = np.array([[1,0,v[0]],[0,1,v[1]],[0,0,1]])
  return T

def getxrotation(d):
  """ Returns rotation matrix Rx in homogeneous coordinates for a rotation of d degrees around the x axis.

  Args:
    d: degrees of the rotation

  Returns:
    Rx: rotation matrix
  """

  #
  # You code here
  #
  Rx = np.array([[1,0,0],
                [0,np.cos(d),np.sin(d)],
                [0,-np.sin(d),np.cos(d)]])
  return Rx

def getyrotation(d):
  """ Returns rotation matrix Ry in homogeneous coordinates for a rotation of d degrees around the y axis.

  Args:
    d: degrees of the rotation

  Returns:
    Ry: rotation matrix
  """

  #
  # You code here
  #

  Ry = np.array([[np.cos(d),0,-np.sin(d)],
              [0,1,0],
              [np.sin(d),0,np.cos(d)]])
  return Ry

def getzrotation(d):
  """ Returns rotation matrix Rz in homogeneous coordinates for a rotation of d degrees around the z axis.

  Args:
    d: degrees of the rotation

  Returns:
    Rz: rotation matrix
  """

  #
  # You code here
  #
  Rz = np.array([[np.cos(d),np.sin(d),0],
              [-np.sin(d),np.cos(d),0],
              [0,0,1]])
  return Rz

def getcentralprojection(principal, focal):
  """ Returns the (3 x 4) matrix L that projects homogeneous camera coordinates on homogeneous
  image coordinates depending on the principal point and focal length.

  Args:
    principal: the principal point, 2d vector
    focal: focal length

  Returns:
    L: central projection matrix
  """

  #
  # You code here
  #

  # [[f,0,px,0],
  #  [0,f,py,0],
  #  [0,0,1,0]]
  L = np.zeros((3,4))
  L[0,0] = focal
  L[1,1] = focal
  L[0,2] = principal[0]
  L[1,2] = principal[1]
  L[2,2] = 1

  return L

def getfullprojection(T, Rx, Ry, Rz, L):
  """ Returns full projection matrix P and full extrinsic transformation matrix M.

  Args:
    T: translation matrix
    Rx: rotation matrix for rotation around the x-axis
    Ry: rotation matrix for rotation around the y-axis
    Rz: rotation matrix for rotation around the z-axis
    L: central projection matrix

  Returns:
    P: projection matrix
    M: matrix that summarizes extrinsic transformations
  """

  #
  # You code here
  #

  # P = K[R|T]
  # Make central projection matrix 3✖3
  L = np.delete(L, -1, axis=1)
  r1 = np.dot(Rx,Ry)
  r2 = np.dot(r1,Rz)
  # Merge rotation matrix and translation matrix
  T = T[:,[-1]]
  r3 = np.hstack((r2,T))
  P = np.dot(L,r3)
  # M = [R|T]
  s1 = np.dot(Rx,Ry)
  s2 = np.dot(s1,Rz)
  M = np.hstack((s2,T))
  return P,M

def projectpoints(P, X):
  """ Apply full projection matrix P to 3D points X in cartesian coordinates.

  Args:
    P: projection matrix
    X: 3d points in cartesian coordinates

  Returns:
    x: 2d points in cartesian coordinates
  """

  #
  # You code here
  #

  # ignore projection matrix
  x = hom2cart(X)
  focal_length = 8
  x[0,0] = x[0,0]/focal_length
  x[1,0] = x[1,0]/focal_length

  return x

def loadpoints():
  """ Load 2D points from obj2d.npy.

  Returns:
    x: np array of points loaded from obj2d.npy
  """

  #
  # You code here
  #

  # Load arrays from ``.npy``
  x = np.load('data/obj2d.npy')
  return x

def loadz():
  """ Load z-coordinates from zs.npy.

  Returns:
    z: np array containing the z-coordinates
  """

  #
  # You code here
  #
  z = np.load('data/zs.npy')
  return z

def invertprojection(L, P2d, z):
  """
  Invert just the projection L of cartesian image coordinates P2d with z-coordinates z.

  Args:
    L: central projection matrix
    P2d: 2d image coordinates of the projected points
    z: z-components of the homogeneous image coordinates

  Returns:
    P3d: 3d cartesian camera coordinates of the points
  """

  #
  # You code here
  #
  L = np.delete(L, 0, axis=1)
  L = np.delete(L, 0, axis=1)
  # P2d 2✖300
  r1 = np.dot(L,P2d)
  # Make z matrix 300✖1
  P3d = np.dot(r1,z.T)
  return P3d

def inverttransformation(M, P3d):
  """ Invert just the model transformation in homogeneous coordinates
  for the 3D points P3d in cartesian coordinates.

  Args:
    M: matrix summarizing the extrinsic transformations
    P3d: 3d points in cartesian coordinates

  Returns:
    X: 3d points after the extrinsic transformations have been reverted
  """

  #
  # You code here
  #

  # Make P3d 4✖1
  P3d = cart2hom(P3d)
  r = np.dot(M,P3d)
  X = cart2hom(r)
  return X

def p3multiplecoice():
  '''
  Change the order of the transformations (translation and rotation).
  Check if they are commutative. Make a comment in your code.
  Return 0, 1 or 2:
  0: The transformations do not commute.
  1: Only rotations commute with each other.
  2: All transformations commute.
  '''

  return -1


# Program 19b: Chaos control in the Henon Map.
# See Figure 19.6.

import matplotlib.pyplot as plt
import numpy as np
# Parameters
a, b = 1.2, 0.4
xstar = ystar = 0.8358
k1, k2 = -1.8, 1.2
num_iterations = 199
rs = []
x, y = 0.5, 0.6

ns = np.arange(num_iterations)
nsc = np.arange(num_iterations, 2*num_iterations)

for n in ns:
    xn = a + b*y - x**2
    yn = x
    x, y = xn, yn
    r = np.sqrt(x**2 + y**2)
    rs.append([n, r])

# Check point is in control region
print(x, y)

for n in nsc:
    xn = -k1 * (x - xstar) - k2 * (y - ystar) + a + b*y - x**2
    yn = x
    x, y = xn, yn
    r = np.sqrt(x**2 + y**2)
    rs.append([n, r])

rs = np.array(rs)

fig, ax = plt.subplots(figsize=(8,8))
plt.plot(rs[:, 0], rs[:, 1])
plt.plot(rs[:, 0], rs[:, 1], 'ro')
plt.xlabel('n', fontsize=15)
plt.ylabel(r'$r_n^2$', fontsize=15)
plt.tick_params(labelsize=15)
plt.show()


# How this problem work ?
# You have a set of points with labels red and green and you want to
# classify a new test point with the correct label
# for that purpose you'll measure the distance between the test point
# and all the train points and your predict label will be the lowest
# distance.

import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter
import numpy as np

# if you're in a notebook you can use
# %matplotlib inline
X_train = np.array([[1,1], [2,2.5],[3,1.2],[5.5,6.3],[6,9],[7,6]])
Y_train = ['red','red','red','blue','blue','blue']
X_test = np.array([3,4])

def dist(x,y):
	return np.sqrt(np.sum((x-y)**2))

num = len(X_train)
distance = np.zeros(num)
for i in range(num):
	distance[i] = dist(X_train[i],X_test)

min_index = np.argmin(distance)
# Making the prediction
print('prediction = ', Y_train[min_index])
# Ploting the data
fig, axs = plt.subplots(1,2, sharex = True)
fig.subplots_adjust(left=0.08, right=0.98, wspace=0.3)

ax = axs[0]
ax.scatter(X_train[:,0], X_train[:,1], s = 170, color = Y_train[:])
ax.scatter(X_test[0], X_test[1],s = 170,color = 'green')
ax.set_title('without prediction')
ax = axs[1]
ax.scatter(X_train[:,0], X_train[:,1], s = 170, color = Y_train[:])
ax.scatter(X_test[0], X_test[1],s = 170,color = Y_train[min_index])
ax.set_title('with prediction')
plt.show()


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

# Creating an array
a1 = np.empty((5, 3))        # content can be anything initially
a2 = np.zeros((5, 3))        # full of zeros
a4 = np.random.rand(5, 3)    # filled with random values between 0 and 1
a3 = np.zeros((5, 3, 4, 2))    # 4-D array
#
## Array size
n_rows = a2.shape[0]            # Number of rows
n_columns = a2.shape[1]         # Number of columns
n_rows, n_columns = a2.shape    # Both at the same time
n_elements = a2.size            # Number of elements (rows*columns)
#
## Obtaining array elements, rows, and columns:
element = a2[0, 0]          # Value from location (1, 1)
second_row = a2[1, :]
third_column = a2[:, 2]
#
## Creating linearly spaced sequences of numbers:
b1 = range(5)               # 0,1,2,3,4 in an iterable
b2 = list(range(1, 5))      # A list containing 1,2,3,4
b3 = np.arange(5)           # A Numpy array containing 0,1,2,3,4
b4 = np.linspace(1, 5, 7)   # Create 7 equally spaced numbers between 1 and 5
#
## Iterating over every row of an array
for row in range(n_rows):
    a2[row, :] = row
#
## Transposing
a2t = a2.T
#
## Reshaping
a2reshaped = np.reshape(a2, (3, 5))
a2reshaped_fortran_order = np.reshape(a2, (3, 5), order='F')
a2flat = a2.flatten()       # 1-D array
#
## Numpy array functions (e.g. mean)
m = np.mean(a2)
m_row = np.mean(a2, axis=0)
m_column = np.mean(a2, axis=1)
## Similarly for many other functions  (sum, fft, std, min, max, ...)
#
## Matrix operations
matrix_product = np.matmul(a2, a2.T)   # Matrix multiplication
elementwise_product = a2 * a2   # Element-wise multiplication
matrix_sum = a2 + a2
mean_subtracted = a2 - m                        # Subtract mean element from every element
mean_subtracted_col = a2 - m_column[:, None]    # Subtract mean of columns from every column
mean_subtracted_row = a2 - m_row
#
## Saving to file and loading from file
np.save('filename.npy', a2)
a2fromfile = np.load('filename.npy')
#
## Matplotlib
plt.plot(a2t)
plt.show()
plt.figure()
plt.plot(np.random.rand(50))
plt.show()
#
#
## EXERCISE task:
## Create two large square matrices (smaller than 3000x3000) filled with random values.
## Then find the diagonal of the matrix product of the two matrices.
## The diagonal elements of matrix product can be obtained in many ways; time different approaches.
## TODO

dim = 1000

a1 = np.random.rand(dim, dim)
a2 = np.random.rand(dim, dim)

begin = time.time()
diagonal = np.diag(np.matmul(a1, a2))
end = time.time();
print('elapsed time = {}'.format(end-begin))

begin = time.time()
diagonal = np.sum(a1 * a2.T, axis = 1)
end = time.time();
print('elapsed time = {}'.format(end-begin))


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

# Copyright (C) 2018 Andreas Franck <a.franck@soton.ac.uk>
# Copyright (C) 2018 University of Southampton

# Code accompanying the paper:

# Andreas Franck and Filippo Maria Fazi, “VISR – A versatile open software
# framework for audio signal processing,” in Proc. Audio Eng. Soc. 2018 Int. Conf.
# Spatial Reproduction, Tokyo, Japan, 2018.

# We kindly ask to acknowledge the use of this software in publications or software
# by citing this paper.

# The code is provided under the ISC (Internet Systems Consortium) license
# https://www.isc.org/downloads/software-support-policy/isc-license/ :

# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

"""
Helper functions for working with vectors.
"""

import numpy as np

def normalise( B, norm=2, axis = -1 ):
    """
    Normalise a vector or a matrix interpreted as a collection of vectors such
    that the norm of each vector is 1.

    Parameters
    ----------

    B: np.ndarray
        Vector or matrix to be normalised.
    norm: int or inf
        p-norm to be used. Default: 2 (l2/Euclidean norm)
    axis: int
        In case of a matrix, the axis index over which the vector normalisation is applied.
        Default: -1 (last dimension)
    Returns
    -------

    np.ndarray, same dimension as B.
    """
    mag = np.linalg.norm( B, ord=norm, axis=axis )
    magExp = np.expand_dims( mag, axis )
    Bnorm = B / magExp
    return Bnorm

def angleDifference( vec1, vec2, axis=-1 ):
    """
    Calculate the angle between between two vectors (or sets of vectors)
    in radian.

    Parameters
    ----------

    vec1: np.ndarray
        First vector or set of vectors (as a matrix). Standard broadcasting rules
        apply.
    vec2: np.ndarray
        Second vector or set of vectors (as a matrix). Standard broadcasting rules
        apply.
    axis: int
        Dimension index of the vectors (in case of a matrix).
        Default: -1 (last dimension)
    Returns:
        Angle in radian, same dimension as the broadcast of vec1 and vec2 minus
        the axis dimension.
    """
    dot= np.sum(normalise(vec1) * normalise(vec2), axis=axis)
    # Clipping is to avoid arguments slightly larger than 1.0 due to numerical accuracy,
    # which would lead to erroneous results of arccos().
    return np.arccos( np.clip(dot, None, 1.0) )


import numpy as np
import matplotlib
matplotlib.use('Agg')
from matplotlib import pyplot as plt

def sim_y(X, beta, beta_zero):
    """Simulate y from X and beta"""
    s = X @ beta - beta_zero
    p = 1. / (1 + np.exp(-s))
    Y = 2*np.random.binomial(1,p) - 1
    return Y

def plot_data(X,Y,beta,beta_zero,filename=None):
    """Plot the 2-dim data with separator line"""
    plt.scatter(X[Y==1,0], X[Y==1,1],marker='$+$')
    plt.scatter(X[Y==-1,0], X[Y==-1,1],marker='$-$')
    ybds = [-beta_zero / beta[1],
            -(beta_zero + beta[0]) / beta[1] ]
    plt.plot([0,1],ybds,'r-')
    plt.xlim([0,1])
    plt.ylim([0,1])
    if filename:
        plt.savefig(filename)
        plt.clf()
    else:
        plt.show()

class Perceptron:
    """
    Rosenblatt's perceptron, online learner

    Attributes:
        eta: learning rate
        beta: coefficient vector
        p: dimension of X
        beta_zero: intercept
    """

    def __init__(self,eta,dim,
                 beta_init=None,beta_zero_init=None):
        """initialize and set beta"""
        self.eta = eta
        self.p = dim
        if beta_init:
            self.beta = beta_init
        else:
            self.beta = np.zeros(dim)
        if beta_zero_init:
            self.beta_zero = beta_zero_init
        else:
            self.beta_zero = 0.

    def predict(self,x):
        """predict y with x"""
        s = x @ self.beta + self.beta_zero
        yhat = 2*(s > 0) - 1
        return yhat

    def update_beta(self,x,y):
        """single step update output 0/1 loss"""
        yhat = self.predict(x)
        if yhat != y:
            self.beta += self.eta * y * x
            self.beta_zero += self.eta * y
        return yhat != y

if __name__=='__main__':
    # Initialize data, beta, simulate y
    n = 200
    X = np.random.uniform(0,1,n*2)
    X = X.reshape((n,2))
    beta = np.array([20,-10])
    beta_zero = 4.
    Y = sim_y(X,beta,beta_zero)
    # Initialize perceptron
    perc = Perceptron(.5,2)
    loss = []
    t_iter = 40
    # Iterate through the data, updating perceptron
    for t,(x,y) in enumerate(zip(X,Y)):
        loss.append(perc.update_beta(x,y))
        if t % t_iter == t_iter - 1:  # Every t_iter plot
            plot_data(X[:t,:],Y[:t],perc.beta,perc.beta_zero,
                      "perc_{}.png".format(t//t_iter))


#
# This program solves Newtons equation for a block sliding on
# an horizontal frictionless surface.
# The block is tied to the wall with a spring, so N's eq takes the form:
#
#  m d^2x/dt^2 = - kx
#
# In order to make the solution dimless, we set k/m = 1.
# This results in two coupled diff. eq's that may be written as:
#
#  dx/dt = v
#  dv/dt = -x
#
# The user has to specify the initial velocity and position,
# and the number of steps. The time interval is fixed to
# t \in [0, 4\pi) (two periods)
#
# Note that this is a highly simplifyed rk4 code, intended
# for conceptual understanding and experimentation.

import sys
import numpy, math

#Global variables
ofile = None;
E0    = 0.0

def sim(x_0, v_0, N):
    ts = 0.0
    te = 4*math.pi
    h = (te-ts)/float(N)

    t = ts;
    x = x_0
    v = v_0
    while (t < te):
        kv1 = -h*x
        kx1 = h*v

        kv2 = -h*(x+kx1/2)
        kx2 =  h*(v+kv1/2)

        kv3 = -h*(x+kx2/2)
        kx3 =  h*(v+kv2/2)

        kv4 = -h*(x+kx3/2)
        kx4 =  h*(v+kv3/2)

        #Write the old values to file
        output(t,x,v)

        #Update
        x = x + (kx1 + 2*(kx2+kx3) + kx4)/6
        v = v + (kv1 + 2*(kv2+kv3) + kv4)/6
        t = t+h

def output(t,x,v):
    de = 0.5*x**2+0.5*v**2 - E0;
    ofile.write("%15.8E %15.8E %15.8E %15.8E %15.8E\n"\
                %(t, x, v, math.cos(t),de));

#MAIN PROGRAM:

#Get input
if len(sys.argv) == 5:
    ofilename =       sys.argv[1];
    x_0       = float(sys.argv[2])
    v_0       = float(sys.argv[3])
    N         =   int(sys.argv[4])
else:
    print "Usage:", sys.argv[0], "ofilename x0 v0 N"
    sys.exit(0)

#Setup
ofile = open(ofilename, 'w')
E0    = 0.5*x_0**2+0.5*v_0**2

#Run simulation
sim(x_0,v_0,N)

#Cleanup
ofile.close()


#Roll no.- Name
#201651007 - Aman Yadav
#201651029 - Mayank Pathela
#201651060 - Anshuman Verma

# Importing needful libraries
import numpy as np

# A function to evaluate Q & R for a given array
def qr(A):
    m, n = A.shape
    Q = np.eye(m)
    for i in range(n - (m == n)):
        H = np.eye(m)
        # Computing matrix Hi
        H[i:, i:] = make_householder(A[i:, i])
        # Finding orthogonal matrix
        Q = np.dot(Q, H)
        # Find new matrix A using Hi
        A = np.dot(H, A)
    return Q, A

def make_householder(a):
    v = a / (a[0] + np.copysign(np.linalg.norm(a), a[0]))
    v[0] = 1
    H = np.eye(a.shape[0])
    H -= (2 / np.dot(v, v)) * np.dot(v[:, None], v[None, :])
    return H

def main():
    # Taking in the number of rows
    m = int(input('\n Please enter the number of rows - '))

    # Taking in the number of columns
    n = int(input('\n Please enter the number of columns - '))

    if m < n:
    	print('\nNumber of rows should be greater than or equal to number of columns')
    	return
    # Creating a matrix of random values with dimensions m,n
    a = np.random.rand(m, n)
    print('\n Original Matrix\n', a)   # Printing original matrix
    q, r = qr(a)    # Evaluating Q and R
    print('\n Q:\n', q.round(6))     # Printing Q
    print('\n R:\n', r.round(6))   # Printing R

    # print(np.matmul(q,r))    #Sample print of multiplication of q,r to check the correctness

if __name__ == '__main__':
    main()


import numpy as np
from scipy.spatial.distance import cdist, euclidean

def mad(data, data_median=None, axis=None):
    """
    Median absolute deviation

    Parameters
    ----------
    data
    data_median

    Returns
    -------

    """
    nans = np.isnan(data)
    if nans.any():
        data = np.ma.MaskedArray(data, nans)

    if data_median is None:
        data_median = np.ma.median(data, axis, keepdims=True)
    # else:
    # make sure we can broadcast them together
    return np.ma.median(np.abs(data - data_median), axis)

def median_scaled_median(data, axis):
    """

    Parameters
    ----------
    data
    axis

    Returns
    -------

    """
    frame_med = np.median(data, (1, 2))[:, None, None]
    scaled = data / frame_med
    return np.median(scaled, axis)

def geometric_median(x, eps=1e-5):
    """
    The geometric median of a discrete set of sample points in a Euclidean space
    is the point minimizing the sum of distances to the sample points. This
    generalizes the median, which has the property of minimizing the sum of
    distances for one-dimensional data, and provides a central tendency in
    higher dimensions. It is also known as the 1-median, spatial median,
    Euclidean minisum point, or Torricelli point.

    Parameters
    ----------
    x: array
        shape: n-points, n-dimensions
    eps: float
        accuracy

    Returns
    -------

    """
    # about: https://en.wikipedia.org/wiki/Geometric_median
    # adapted from: https://stackoverflow.com/a/30305181/1098683

    # ensure we have an array
    x = np.asanyarray(x)
    ndim = x.ndim

    # make sure we have some data
    assert x.size

    # remove any masked points along trailing dimensions
    if np.ma.is_masked(x):
        x = x[~x.mask.any(tuple(range(1, ndim)))].data
        # if all points are masked, return masked array
        if not x.size:
            return np.ma.masked_all(ndim - 1, x.dtype)

    # starting point: sample mean
    y = x.mean(0)

    while True:
        dist = cdist(x, [y])    # distance to mean
        non_zeros = (dist != 0)[:, 0]

        dist_inv = 1 / dist[non_zeros]
        dist_inv_sum = np.sum(dist_inv)
        w = dist_inv / dist_inv_sum
        T = np.sum(w * x[non_zeros], 0)

        num_zeros = len(x) - np.sum(non_zeros)
        if num_zeros == 0:
            y1 = T
        elif num_zeros == len(x):
            return y
        else:
            R = (T - y) * dist_inv_sum
            r = np.linalg.norm(R)
            rinv = 0 if r == 0 else num_zeros / r
            y1 = max(0, 1 - rinv) * T + min(1, rinv) * y

        if euclidean(y, y1) < eps:
            return y1

        y = y1