Example #1
0
def runMarginalFW(G,alg_params,model=None,V = [],alpha = [],stepSizeComputation = False,rho_vec =-1):
	quiet = alg_params['quiet'] 
	if stepSizeComputation:
		quiet = True
		assert type(rho_vec)!=int,'Requires rho_vec to be a vector'
		G.rhos_edge = rho_vec
		G.computeNodeRhos()
	######### Setup parameters $$$$$$$$$$
	stdout_initial = -1
	dev_null_f = open(os.devnull,'w')
	if quiet:
		stdout_initial = sys.stdout
		sys.stdout = dev_null_f
	logf,matf,fxn_params,f_obj = setupParams(G,alg_params)
	#print "\t----- Starting Marginal Inference with FW ------ \t"

	#Initial settings
	mus = G.init_vec
	MAX_STEPS = alg_params['max_steps']
	smallest_marginal = np.Inf
	if len(alpha)==0:
		V.append(csr_matrix(mus))
		alpha.append(1)

	#Track statistics
	statistics= setupStatistics(MAX_STEPS,mus.shape,G.nVertices)
	statistics['alpha'] = alpha

	#Extract gurobi model 
	if not alg_params['useMAP'] and model is None:
		model = ILPsolver.defineModel(G,alg_params)

	if alg_params['useMAP'] and alg_params['MAPsolver']!='toulbar2':
		len_gap = 5
	else:
		len_gap = 1
	objWarning =0
	gap_l = [10]*len_gap

	if alg_params['M_truncated_dynamic'] or alg_params['M_truncated']:
		#Additional stats to track 
		#Updated at every iteration 
		statistics['gap_FW'] = []
		statistics['gap_full'] = []
		#Updated when epsilon modified 
		statistics['eps_val'] = []
		statistics['primal_push'] = []
		statistics['iterate_push'] = []
		statistics['marker'] = []
		statistics['eps_val'] =[alg_params['M_eps']]
		#print "------------- M_eps with eps = ",alg_params['M_eps'],' -----------------'

	if alg_params['PreCorrection']:
		alg_params['correction_tol'] = 0.5
		G.init_vec = mus
		mus,val,bound,correctionGap = optutil.corrective(G,alg_params,V,alpha,np.inf,logf)

	#For all the steps
	for it in xrange(MAX_STEPS):
		assert len(alpha)==len(V),'mismatch in alpha and V'
		start_time = time.time()
		val,grad = f_obj(mus,fxn_params,True)
		#################   Running MAP Inference ################
		if not alg_params['useMAP']:
			model = ILPsolver.updateObjective(grad,alg_params,model)
		if alg_params['useMAP']:
			map_potentials = -1*grad
			if alg_params['MAPsolver']=='toulbar2':
				vertex,toulbar2_output,solution_MAP = MAPsolver.runMAP(map_potentials,G.nVertices,G.Edges,
					G.Cardinality,G.graphType,alg_params['toulbar2_uai_file'],alg_params['maxSecondsPerMAP'])
			elif alg_params['MAPsolver']=='MPLP':
				vertex,MPLP_output,solution_MAP = MPLPsolver.runMAP(map_potentials,G.nVertices,G.Edges,
					G.Cardinality,G.graphType,alg_params['toulbar2_uai_file'],alg_params['maxSecondsPerMAP'])
			else:#Use MAP solvers from openGM
				if it<2:
					vInit = None
				else:
					vInit = V[-1].toarray()[0]
				vertex,MAP_output,solution_MAP = GenericSolver.runMAP(map_potentials,G.nVertices,G.Edges,
						G.Cardinality,G.graphType,alg_params['toulbar2_uai_file'],'all',vInit,logf)
		else:
			vertex = ILPsolver.optimize(model)
		################   Code to track usage and save statistics ##################
		if alg_params['use_marginal_polytope']:
			assert np.sum(vertex-vertex.astype(int))<np.exp(-10),"Vertex not integer. Investigate : "+str(vertex)
		###############   Update marginals ###############
		#ipdb.set_trace()

		#Check if epsilon needs to be updated 
		if alg_params['M_truncated_dynamic']:
			g_u0 = np.dot(-1*grad,alg_params['uniform']-mus)
			g_k = np.dot(-1*grad,vertex-mus)
			#If the gap is negative use the correctionGap
			if g_k<0:
				g_k = correctionGap 
			if g_u0==0:
				new_eps = np.inf
			elif g_u0<0:
				new_eps = g_k/(-4*g_u0)
			else:
				new_eps = alg_params['M_eps']
			#Halve epsilon if the gap is *still* negative (shouldn't happen)
			#and if below precision -> set to 0 
			if new_eps < 0:
				new_eps = alg_params['M_eps']/2.
				if new_eps<1e-15:
					new_eps = 0
			if new_eps<alg_params['M_eps']:
				#Modified version to ensuring halving
				new_eps = min(new_eps,alg_params['M_eps']/2.)
				print "************ UPDATING DYN. EPSILON TO ",new_eps," *******************"
				old_eps = alg_params['M_eps']
				for i in xrange(1,len(alpha)):
					alpha[i] = alpha[i]*((1-old_eps)/(1-new_eps))
				alpha[0] = alpha[0] - (1-alpha[0])*(((1-old_eps)/(1-new_eps))*new_eps - old_eps)
				alg_params['M_eps'] = new_eps 
				#Check what an away step would look like 
				step_size,min_fxn_val = optutil.getStepDir(f_obj,fxn_params, mus-alg_params['uniform'],mus,0,alpha[0]/(1-alpha[0]))
				statistics['primal_push'].append(min_fxn_val)
				statistics['iterate_push'].append(mus+step_size*(mus-alg_params['uniform']))
				statistics['eps_val'].append(new_eps)
				statistics['marker'].append(it)
				alg_params['correction_tol'] = 0.5
				G.init_vec = mus
				mus,val,bound,correctionGap = optutil.corrective(G,alg_params,V,alpha,np.inf,logf)

		if alg_params['pairwise']:
			mus,gap,direction,step_size,extra = optutil.PFWstep(mus,grad,vertex,V,alpha,alg_params,f_obj,fxn_params)
		else:
			mus,gap,direction,step_size,extra = optutil.FWstep(mus,grad,vertex,V,alpha,alg_params,f_obj,fxn_params)
			#if is eps -> uses extra['gap_FW'] otherwise uses gap wchich is the FW gap anyways

		#Fully Corrective Variant
		if np.mod(it,alg_params['correctionFreq'])==0 and alg_params['doCorrection']:
			G.init_vec = mus
			if gap < 1:
				alg_params['correction_tol'] = 0.05
			else:
				alg_params['correction_tol'] = 0.5
			mus,val,bound,correctionGap = optutil.corrective(G,alg_params,V,alpha,np.inf,logf)
		#Local Search
		if alg_params['doICM']:
			mus,vertex_last,val = optutil.localsearch(vertex,mus,G,alg_params,f_obj,
				fxn_params,V,alpha,logf)

		#Track statistics
		entropy = np.sum(-1*np.log(mus)*mus)
		val_do_not_use,grad_final = f_obj(mus,fxn_params,True)
		grad_norm = np.max(grad_final)
		dir_norm = np.linalg.norm(direction)		
		if np.min(mus)<smallest_marginal:
			smallest_marginal = np.min(mus)
		updateStatistics(statistics,it,time.time()-start_time,len(V),mus,val,gap,step_size,entropy,grad_norm,dir_norm,np.min(mus))

		#Also track statisitcs for 
		if alg_params['M_truncated'] or alg_params['M_truncated_dynamic']:
			statistics['gap_FW'].append(extra['gap_FW'])
			statistics['gap_full'].append(extra['gap_full'])
			writeStatistics(statistics,it,matf,alsoWrite = ['gap_FW','gap_FW'])
			print 'Gap FW: ',extra['gap_FW']
		else:
			writeStatistics(statistics,it,matf)

		#Error : Objective not decreasing
		if it>2 and statistics['Obj_Val'][it]>statistics['Obj_Val'][it-1] and np.abs(statistics['Obj_Val'][it]-statistics['Obj_Val'][it-1])>1e-7:
			logf.write('####### WARNING. OBJECTIVE NOT DECREASING ######\n')
			logf.write('Step Size: '+str(step_size)+' Diff: '+str(np.abs(statistics['Obj_Val'][it]-statistics['Obj_Val'][it-1]))+'\n')
			print "WARNING: ",'Step Size: '+str(step_size)+'Obj: '+str(statistics['Obj_Val'][it])+' Diff: '+str(np.abs(statistics['Obj_Val'][it]-statistics['Obj_Val'][it-1]))+'\n'
			objWarning +=1
			if objWarning>3:
				logf.write('#### EXITING due to increasing objective #####\n')
				break
			assert False,'Objective should be decreasing'

		################## Print/Write to console ##################
		entropy_mus = np.sum(obj_fxn.computeEntropy(mus[1:np.sum(G.Cardinality)]))
		print "\n"
		status_1 = 	'It: %d, Primal: %.8g, Gap: %.8g, Bound: %.5g ' % (it,val,gap,statistics['Bound'][-1])
		status_2 = 	'Time(s): %.3g, StepSize: %.8g  ' % (statistics['Runtime'][-1],step_size)
		status_3 = 	'Entropy(mus)= %.3g, Norm (p) : %.3g, Norm (g) : %.3g '% (entropy_mus,dir_norm,grad_norm)
		status_4 =  'Min mu (overall) = %.5g, Min mu (current) = %.5g Sum(alphas) = %.3g' %(smallest_marginal,np.min(mus),np.sum(alpha))
		status = status_1+status_2+status_3+status_4
		print status
		logf.write(status+'\n')

		##################  Check stopping criterion ################
		gap_l[np.mod(it,len_gap)] = gap
		#M_eps variants have different stopping criterion 
		if alg_params['M_truncated'] or alg_params['M_truncated_dynamic']:
			#All variants break if global gap is less than tolerance 
			if extra['gap_FW']<alg_params['tol']:
				break 
			#Modify eps 
			if alg_params['M_truncated'] and alg_params['M_eps_iterations']>1 and gap < alg_params['tol']: #Manually modify eps
				old_eps = alg_params['M_eps']
				new_eps = alg_params['M_eps']/2.0
				print "************ UPDATING EPSILON TO ",new_eps," *******************"
				for i in xrange(1,len(alpha)):
					alpha[i] = alpha[i]*((1-old_eps)/(1-new_eps))
				alpha[0] = alpha[0] - (1-alpha[0])*(((1-old_eps)/(1-new_eps))*new_eps - old_eps)
				alg_params['M_eps'] = new_eps 
				#Check what an away step would look like 
				step_size,min_fxn_val = optutil.getStepDir(f_obj,fxn_params, mus-alg_params['uniform'],mus,0,alpha[0]/(1-alpha[0]))
				statistics['primal_push'].append(min_fxn_val)
				statistics['iterate_push'].append(mus+step_size*(mus-alg_params['uniform']))
				statistics['eps_val'].append(new_eps)
				statistics['marker'].append(it)
				alg_params['M_eps_iterations'] = alg_params['M_eps_iterations'] - 1
		else:
			if np.max(gap_l)<alg_params['tol']:
				print "Duality Gap condition reached: ",np.mean(gap_l)
				break

	print "\n----- Done Marginal Inference ------ \n"
	################ Cleanup Code ###############
	if alg_params['M_truncated'] or alg_params['M_truncated_dynamic']:
		old_eps = alg_params['M_eps']
		new_eps = alg_params['M_eps']/2.0
		for i in xrange(1,len(alpha)):
			alpha[i] = alpha[i]*((1-old_eps)/(1-new_eps))
		alpha[0] = alpha[0] - (1-alpha[0])*(((1-old_eps)/(1-new_eps))*new_eps - old_eps)
		alg_params['M_eps'] = new_eps 
		#Check what an away step would look like 
		#print alpha[0]
		step_size,min_fxn_val = optutil.getStepDir(f_obj,fxn_params, mus-alg_params['uniform'],mus,0,alpha[0]/(1-alpha[0]))
		statistics['primal_push'].append(min_fxn_val)
		statistics['iterate_push'].append(mus+step_size*(mus-alg_params['uniform']))
		statistics['eps_val'].append(new_eps)
		statistics['marker'].append(it)

		mat = writeStatistics(statistics,it,matf,returnMAT = True,alsoWrite = ['gap_FW','gap_full','primal_push','eps_val','marker'])
		mat['iterate_push'] = np.vstack(statistics['iterate_push'])
	else:
		mat = writeStatistics(statistics,it,matf,returnMAT = True)

	print "LogZ: ",val*-1+gap
	print "Marginals: ",
	#for m in statistics['IterSet'][it,:np.sum(G.Cardinality)].tolist():
	#	print ('%.4f')%(m),
	n_m = statistics['IterSet'][it,:np.sum(G.Cardinality)]
	print n_m.reshape(G.nVertices,2)
	e_m = statistics['IterSet'][it,np.sum(G.Cardinality):]
	print e_m.reshape(G.nEdges,4)

	dev_null_f.close()
	logf.close()

	if quiet:
		sys.stdout=stdout_initial
	if stepSizeComputation:
		return val*-1+gap
	else:
		return mat
def optSpanningTree(G,spanning_tree_params):
	quiet = spanning_tree_params['quiet']
	dev_null_f = open(os.devnull,'w')
	if quiet:
		stdout_initial = sys.stdout
		sys.stdout = dev_null_f
	print "\n----- Starting Marginal Inference - Optimizing Spanning Tree Polytope ------ \n"

	#Dealing with parameters
	alg_params = copy.deepcopy(spanning_tree_params['alg_params'])
	spanning_tree_iter = spanning_tree_params['spanning_tree_iter']
	matf = spanning_tree_params['mat_file_name']
	logfname = spanning_tree_params['log_file_name']

	assert 'usePreprocess' in spanning_tree_params,'usePreprocess not set'
	assert 'FWStrategy' in spanning_tree_params and 'Kval' in spanning_tree_params,'FWStrategy/Kval not set'
	assert 'stepSizeComputation' in spanning_tree_params,'stepSizeComputation not set'

	model = None
	if not alg_params['useMAP']:
		model = ILPsolver.defineModel(G,alg_params)

	#Keep aside a different log/mat file for every iteration of optimizing over the spanning tree
	alg_matf = alg_params['mat_file_name'].replace('.mat','')
	alg_logf = alg_params['log_file_name'].replace('.txt','')

	logf = open(logfname,'w',0)

	#Tracking unique vertices of the marginal polytope
	all_data = []
	vec_len = max(G.init_vec.shape)

	V = []
	alpha = []
	prevUb = np.inf

	########### Running iterations of Frank-Wolfe #################
	for it in xrange(spanning_tree_iter):
		print "\nIteration ",(it+1)

		#Update the name of matlab/log file for every iteration
		alg_params['mat_file_name'] = alg_matf+'-sp'+str(it)+'.mat'
		alg_params['log_file_name'] = alg_logf+'-sp'+str(it)+'.txt'

	################# Strategies for marginal inference with FW #############################
		if spanning_tree_params['FWStrategy'] == 'runK' and it>0:
			#Truncate the number of ILP calls
			alg_params['max_steps'] = spanning_tree_params['Kval']
		elif spanning_tree_params['FWStrategy'] == 'uniform':
			pass
		else:
			pass

		if spanning_tree_params['usePreprocess'] and it>0:
			alg_params['PreCorrection'] = True 
		else:
			alg_params['PreCorrection'] = False 
		
		mat = inf.runMarginalFW(G,alg_params,model,V,alpha)
		if len(alpha) != len(mat['alpha']):
			alpha = mat['alpha']
		val = mat['Obj_Val'][-1]
		mus = mat['IterSet'][-1,:]
		#postprocess
		start_time = time.time()
		mat['Runtime'][-1] = mat['Runtime'][-1]+(time.time()-start_time)
	########################   Update Status ####################################

		#print Status
		status = 'Stats: #Itns: %d, Primal Obj: %.6f,\
				Final Gap: %.6f, Avg Runtime(seconds): %.3f,\
				Last Step Size: %f, #Unique Vertices : %d' % \
				(mat['Obj_Val'].shape[0],mat['Obj_Val'][-1],
				mat['Dual_Gap'][-1],np.mean(mat['Runtime']),
				mat['Step_Size'][-1],len(V))
		#print status
		if quiet:
			stdout_initial.write(str(os.getpid())+" "+str(status)+"\n")

		logf.write(status+'\n')

	#####################  Updating rho vectors ###############################
		start_time = time.time()
		G.init_vec  = mus
		spanning_tree_polytope_vertex,grad = spanUtil.computeMST(G,mus)
		direction = np.reshape(spanning_tree_polytope_vertex,(spanning_tree_polytope_vertex.shape[0],1))-G.rhos_edge
		rhos = G.rhos_edge
		status = "Computing directed spanning tree took "+str(time.time()-start_time)+' seconds'
		#print status
		logf.write(status+'\n')
		#Strategies for computing the step size
		if spanning_tree_params['stepSizeComputation'] == 'linesearch':
			G_copy = copy.deepcopy(G)
			alpha_rho = sciopt.fminbound(lambda a:inf.runMarginalFW(G_copy,alg_params,model,V,alpha,stepSizeComputation=True,rho_vec = rhos+a*direction),0,1,xtol=0.0005)#,disp=3)
			print "Step Size (Vertices) :",alpha_rho
		elif spanning_tree_params['stepSizeComputation'] == 'standard':
			alpha_rho = float(2)/(it+3)
			print "Step Size (Standard) :",alpha_rho
		else:
			assert False,'Invalid stepSizeComputation: '+spanning_tree_params['stepSizeComputation']
		#Update
		G.rhos_edge = G.rhos_edge+alpha_rho*(direction)
		#Update rhos_node and K_C
		G.computeNodeRhos()
		print "Edge Appearance\n",G.rhos_edge," \nDirection\n",spanning_tree_polytope_vertex
		#print "LogZ estimate: ",inf.runMarginalFW(G_copy,alg_params,model,V,alpha,stepSizeComputation=True,rho_vec = rhos+alpha_rho*direction)
	############################# Collect Data and Save ###############################
		#Collect data on this iteration
		data = {}
		data['fw_result'] = mat
		data['rhos'] = G.rhos_edge
		data['alpha_rho'] = alpha_rho
		data['timeTaken'] = time.time()-start_time + np.sum(mat['Runtime'])
		data['dualityGap'] = -1*np.dot(grad,direction) 
		print "Rho Gap: ",data['dualityGap']

		#Postprocessing using the vertices of the marginal polytope
		if spanning_tree_params['usePreprocess'] and it<spanning_tree_iter-1:
			start_time = time.time()
			mus,val,prevUb,gapFW = optutil.corrective(G,alg_params,V,alpha,bound=prevUb)
			G.init_vec = mus
			data['timeTaken'] += (time.time()-start_time)

		#Append stats at the very end
		all_data.append(data)
		all_data_mat = {}
		all_data_mat['final_mus'] = mus
		all_data_mat['final_logz'] = val
		all_data_mat['rho_data'] = all_data
		all_data_mat['MARG_vertices'] = scipy.sparse.vstack(V)
		savemat(matf,all_data_mat)

	print "\n----- Done Marginal Inference ------ \n"
	logf.close()
	if quiet:
		sys.stdout=stdout_initial
	dev_null_f.close()