#drugi labos APR - potupak po Boxu
__author__= "Melita Mihaljevic"
__date__= "2008"
__version__= "0.1"

import random 		
import sys			
import math						

class Box:

       	def __init__(self,X0,e=1e-9,a=1.3):                 # konstruktor   
          	"""konstruktor klase za postupak po boxu 
		   parametri su e- preciznost,a- koeficijent releksije i pocetna tocka"""
          	self.Xd = -100                      	# eksplicitna ogranicenja intervala[-100,100]
          	self.Xg = 100
          	self.X0 = []                        	# pocetna tocka - u dimenzijama
          	self.Xc=[]                      	#centorid
		self.X=[]				
		self.n					#broj dimenzija
		self.X0=X0				#pocetna tocka
		self.e=e				#preciznost
		self.Xr= []				#tocka refleksije
		self.a=a				#koeficijent refleksije
      	
	def g(self,x):
          	""" implicitna ogranicena za funkciju 
		ulazni parametar je tocka a izlazni je istinitosna vrijednost provjere implicitnih ogranicenja"""
           	return ((x[0]-x[1]<=0) or (x[0]-2<=0))

        def h(self,X):
           	"""eksplicitna ogranicenja za funkciju
		ulazni parametar je tocka, a izlazni je provjera eksplicitnih ogranicenja po svakoj dimenziji"""
         	for i in X:
            		if i>= self.Xg or i<= self.Xd:
             			return 0
         	return 1

	def n(self):
		"""dimenzija """
		self.n= len(self.X0)


        def find_minimum(self,funkcija):
        	"""funkcija za pronalazenja minimuma funkcije
		ulazni parametar je ime funkcije,a izlazni je tocka minimuma
		sastoji se od 3 dijela: - generiranje tocaka, refleksija i provjera uvjeta zaustavljanja"""

		self.n()							# dimenzije tocke
		fj=funkcija							# funkcija nad kojom se provodi minimizacija
          	if not(self.h(self.X0)):                                       	# provjera implicinih ogranicenja X0
              		print"Pocetna tocka ne zadovoljava eksplicitna ogranicenja [-100,100]"
             		sys.exit(0)

       		if not(self.g(self.X0)):                                       	# provjera eksplicitnih ogranicenja X0
	            	print"Pocetna tocka ne zadovoljava implicitna ogranicenja"
			sys.exit(0)

                self.Xc= self.X0                                    		# cenroid je pocetna tocka X0
		self.X=[[0 for i in xrange(self.n)]for i in xrange(2*self.n)]	# inicijalizacija X-a tj skupa tocaka

#(1) GENERIRANJE TOCAKA:
										# generiranje skupa od 2n tocaka
		for t in xrange(2*self.n):
			for i in xrange(self.n):			# stvori novu tocku za koju je zadovoljen uvjet
				R= random.uniform(0,1)				# biram random broj [0,1]
				self.X[t][i]= self.Xd + R*(self.Xg-self.Xd)	# nova tocka
				while (not self.g(self.X[i])):			# dok nisu zadovoljeni implicitni uvjeti
					for j in xrange(len(self.X[t])):        # pomakni novu tocku prema centroidu
                  	                      	self.X[t][j]= 0.5*(self.X[t][j] + self.Xc[j])
						if abs(self.X[t][j])< self.e:	# provjeri jel ima smisla dalje ici
							print 'promasio si totalno ne mogu zadovoljiti uvijet'
							sys.exit(0)   		# ako nema smisla ici dalje zaustavi 										

				for j in xrange(self.n):			# racunaj novi centroid
					self.Xc[j]= 0
					for k in xrange(t):
						self.Xc[j]= self.Xc[j]+self.X[k][j]
			
#(2) REFLEKSIJE:	
		# postupak refleksije dok ne dodje do uvjeta zaustavljanja	
		while (1):
			max1 = 0					# koordinate dva najveca maximuma
			max2 = 0
									# pronalazak najgoreg i najgoreg drugog
			for t in xrange(len(self.X)):
				curr = self.f(fj,self.X[t]) 		# izracunaj funkcijsku vrijednost 	
				if curr > self.f(fj,self.X[max2]):	# provjeri jel ima veca
					if curr > self.f(fj,self.X[max1]):
						max2= max1		# zamijena da mi ostane najgori i onaj drugi 
						max1= t				
					else:
						max2=t
									# izracunaj Xc bez Xh:
			for j in xrange(self.n):
				self.Xc[j]=0				
				for k in xrange(len(self.X)):
					if k != max1:
						self.Xc[j]= self.Xc[j]+ self.X[k][j]
				self.Xc[j]= self.Xc[j]/(len(self.X)-1)

										# refleksija- sam postupak
			self.Xr=[0 for i in xrange(self.n)]			# inicijalizacija tocke refleksije
			for j in xrange(self.n):
				self.Xr[j]= (1+self.a)*self.Xc[j]-self.a*self.X[max1][j]

			# pomicanje na granice explicitnih ogranicenja ako su predjene granice
			for i in xrange(self.n):
				if self.Xr[i] < self.Xd:
					self.Xr[i]=self.Xd
				if self.Xr[i]>self.Xg:
					self.Xr[i]= self.Xg
			
			#provjerimo implicitna ogranicenja i povlacimo se prema centroidu ako nisu zadovoljena:
			if self.f(fj,self.Xr)> self.f(fj,self.X[max2]):
				for j in xrange(self.n):
					self.Xr[j] = 0.5 * (self.Xr[j] + self.Xc[j])

			#zamijeni u skupu tocaka najgoru i novu reflektiranu	
			self.X[max1]=self.Xr 
				
#(3) UVJET ZAUSTAVLJANJA
			uvijet= 0
			for t in xrange(len(self.X)):
				b= 0
				for j in xrange(self.n):
					b= abs(self.X[t][j]- self.Xc[j])
				uvijet = uvijet+b*b
			uvijet = uvijet/self.n
			uvijet = math.sqrt(uvijet)
			if uvijet < self.e:
				return self.Xr			# vrati zadnji Xr koji je nadjen a to je minimum funkcije

#definicija funkcija f2 i f3 s labosa:

	def fun_f2(self,X):
		"""funkcija f2 za provjeru"""
		return (math.pow((X[0]-4),2)+4*math.pow((X[1]-2),2))		#vraca prvu funkciju

	def fun_f3(self,X):							#p je polje
		"""funkcija f3 za provjeru"""
		f=open("parm","r")
		p=[]
		for line in f:
			p.append(int(line))
		f.close()
		return (math.pow((X[0]-p[0]),2)+ math.pow((X[1]-p[1]),2)+ math.pow((X[2]-p[2]),2) + math.pow((X[3]-p[3]),2)+ math.pow((X[4]-p[4]),2))

	def f(self,funk,X):
		"""funkcija za funkciju"""
		
		if funk== "f2":
			x= self.fun_f2(X)
		elif funk =="f3":
			x= self.fun_f3(X)
		return x	

				
# glavni program:
argumenti= sys.argv
	
if len(argumenti) !=3:
	print "krivo pozvan program - program pozvati sa: python box_algorithm.py ime_funkcije pocetna_tocka"
	print "primjer: python box_algorithm.py f2 1,2,3,4"
	sys.exit(0)

ime_funkcije= argumenti[1]
poc=argumenti[2]
x0=[]
m=poc.split(',')
for i in m:
	x0.append(int(i))

b= Box(x0)
xr=b.find_minimum(ime_funkcije)		
print 'Minimum funkcije',ime_funkcije,'je:',xr

