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 pandas
from sklearn.linear_model import SGDRegressor

"""
In this question, you need to:
1) Implement the linear_regression() procedure using gradient descent.
   You can use the SGDRegressor class from sklearn, since this class uses gradient descent.
2) Select features (in the predictions procedure) and make predictions.

"""

def normalize_features(features):
    '''
    Returns the means and standard deviations of the given features, along with a normalized feature
    matrix.
    '''
    means = np.mean(features, axis=0)
    std_devs = np.std(features, axis=0)
    normalized_features = (features - means) / std_devs
    return means, std_devs, normalized_features

def recover_params(means, std_devs, norm_intercept, norm_params):
    '''
    Recovers the weights for a linear model given parameters that were fitted using
    normalized features. Takes the means and standard deviations of the original
    features, along with the intercept and parameters computed using the normalized
    features, and returns the intercept and parameters that correspond to the original
    features.
    '''
    intercept = norm_intercept - np.sum(means * norm_params / std_devs)
    params = norm_params / std_devs
    return intercept, params

def linear_regression(features, values):
    """
    Perform linear regression given a data set with an arbitrary number of features.
    """
    #features = sm.add_constant(features)
    model = SGDRegressor(n_iter=30)
    #normalize_res = normalize_features(features)
    model.fit(features,values)

    ###########################
    ### YOUR CODE GOES HERE ###
    ###########################
    intercept = model.intercept_
    params = model.coef_
    return intercept, params

def predictions(dataframe):
    '''
    The NYC turnstile data is stored in a pandas dataframe called weather_turnstile.
    Using the information stored in the dataframe, let's predict the ridership of
    the NYC subway using linear regression with gradient descent.

    You can download the complete turnstile weather dataframe here:
    https://www.dropbox.com/s/meyki2wl9xfa7yk/turnstile_data_master_with_weather.csv

    Your prediction should have a R^2 value of 0.40 or better.
    You need to experiment using various input features contained in the dataframe.
    We recommend that you don't use the EXITSn_hourly feature as an input to the
    linear model because we cannot use it as a predictor: we cannot use exits
    counts as a way to predict entry counts.

    Note: Due to the memory and CPU limitation of our Amazon EC2 instance, we will
    give you a random subset (~50%) of the data contained in
    turnstile_data_master_with_weather.csv. You are encouraged to experiment with
    this exercise on your own computer, locally.

    If you receive a "server has encountered an error" message, that means you are
    hitting the 30-second limit that's placed on running your program. Try using a
    smaller number of features or fewer iterations.
    '''
    ################################ MODIFY THIS SECTION #####################################
    # Select features. You should modify this section to try different features!             #
    # We've selected rain, precipi, Hour, meantempi, and UNIT (as a dummy) to start you off. #
    # See this page for more info about dummy variables:                                     #
    # http://pandas.pydata.org/pandas-docs/stable/generated/pandas.get_dummies.html          #
    ##########################################################################################
    features = dataframe[['rain', 'precipi', 'Hour', 'meantempi']]
    dummy_units = pandas.get_dummies(dataframe['UNIT'], prefix='unit')
    features = features.join(dummy_units)

    # Values
    values = dataframe['ENTRIESn_hourly']

    # Get numpy arrays
    features_array = features.values
    values_array = values.values

    means, std_devs, normalized_features_array = normalize_features(features_array)

    # Perform gradient descent
    norm_intercept, norm_params = linear_regression(normalized_features_array, values_array)

    intercept, params = recover_params(means, std_devs, norm_intercept, norm_params)

    predictions = intercept + np.dot(features_array, params)
    # The following line would be equivalent:
    # predictions = norm_intercept + np.dot(normalized_features_array, norm_params)

    return predictions


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))


import numpy as np

class MatrixGeneration(object):
    @staticmethod
    def exchange_matrix(n):
        E = np.zeros((n, n))
        for r in range(n):
            for c in range(n):
                if c == n - r + 1:
                    E[r, c] = 1
        return E

    @staticmethod
    def skew_symmetric_matrix(x):
        if len(x) == 3:
            S = np.array([[0, -x[2], x[1]],
                          [x[2], 0, -x[0]],
                          [-x[1], x[0], 0]])
            return S
        else:
            raise ValueError('Dimension {} is not supported. \
                              Use only 3 instead'.format(len(x)))

    def random_rotation_matrix(self, dim=3):
        if dim == 2:
            theta = np.random.random()
            c, s = np.cos(2 * np.pi * theta), np.sin(2 * np.pi * theta)
            R = np.array([[c, -s], [s, c]])
            return R
        elif dim == 3:
            u = np.random.uniform(size=(dim,))
            if np.linalg.norm(u) > 0:
                u = u / np.linalg.norm(u)
                S = self.skew_symmetric_matrix(u)
                In = np.eye(dim)
                R = np.dot((In - S), np.linalg.inv(In + S))
                return R
        else:
            raise ValueError('Dimension {} is not supported. \
                              Use 2 or 3 instead'.format(dim))

    @staticmethod
    def constant_matrix(self, size, c):
        m, n = size
        C = np.zeros((m, n))
        C[:, :] = c
        return C


# -*- 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()