class KM_Solver(object): def __init__(self, data_mat=None, W=None, H=None, res_dir=None, rank=4, SNR=-5, seed_num=1, true_labels=None): if data_mat is None or W is None or H is None or res_dir is None: raise ValueError('Error: some inputs are missing!') self.data_mat = data_mat self.W, self.H = W, H self.rank = rank self.SNR = SNR self.res_dir = res_dir self.seed_num = seed_num self.converge = Convergence(res_dir) self.labels = true_labels np.random.seed( seed_num ) # set the seed so that each run will get the same initial values (m, n) = self.data_mat.shape self.flag = 0 # flag to indicate whether to use LS or gradient descent to update W m_name = 'km' + str(self.flag) self.output_dir = path.join(self.res_dir, 'onmf', m_name, 'rank' + str(self.rank), 'data' + str(SNR), 'seed' + str(self.seed_num)) self.time_used = 0 # record the time elapsed when running the simulations def set_max_iters(self, num): self.converge.set_max_iters(num) def set_tol(self, tol): self.converge.set_tolerance(tol) def get_nmf_cost(self, W, H): res = LA.norm(self.data_mat - W * H, 'fro')**2 return res def get_iter_num(self): return self.converge.len() def update_scheme(self, verbose=False): ''' The function performs the update of W and H using similar ways as that in K-means Specifically, for each column of H, h_j, it will try to consider each pos as the place holding non-zero entry For example, if H_{k, j} != 0, then H_{k, j} = arg min_{c > 0} ||x_j - w_k * c||_2^2 which leads to H_{k, j} = (x_j^T w_k) / ||w_k||_2^2 if w_k != 0, and otherwise, = 1 By trying all k = 1, ..., K. we obtain h_j with lowest obj value for each column of W, w_k, w_k = arg min_{ w >= 0} sum_{j \in C_k} (x_j - w * H_{k, j})^2 which leads to w_k = X * ~h_k^T / ||~h_k||_2^2 ''' # update H #p_cost = self.get_nmf_cost(self.W, self.H) (ha, hb) = self.H.shape H_pre = np.asmatrix(np.copy(self.H)) for j in range(hb): tmp = LA.norm(self.data_mat[:, j] - self.W * H_pre[:, j], 2)**2 p_cost = self.get_nmf_cost(self.W, self.H) for k in range(ha): h_j_new = np.asmatrix(np.zeros((ha, 1))) #print h_j_new #print h_j_new if LA.norm(self.W[:, k], 2) == 0: #print 'the k th column of W is 0' h_j_new[k, 0] = 1 else: h_j_new[k, 0] = self.data_mat[:, j].transpose( ) * self.W[:, k] / (LA.norm(self.W[:, k], 2)**2) # check if a smaller obj value is obtained val = LA.norm(self.data_mat[:, j] - self.W * h_j_new, 2)**2 #print 'val: ' + str(val) + ', tmp: ' +str(tmp) if val < tmp: self.H[:, j] = np.copy(h_j_new) tmp = val ''' c_cost = self.get_nmf_cost(self.W, self.H) if c_cost > p_cost: print 'cur cost: ' + str(c_cost) + ', p_cost: ' + str(p_cost) print H_pre[:, j] print self.H[:, j] print LA.norm(self.data_mat[:, j] - self.W * H_pre[:, j], 'fro') ** 2 print LA.norm(self.data_mat[:, j] - self.W * self.H[:, j], 'fro') ** 2 print '------' print LA.norm(self.data_mat[:, 0:2] - self.W * H_pre[:, 0:2], 'fro') ** 2 print LA.norm(self.data_mat[:, 0:2] - self.W * self.H[:, 0:2], 'fro') ** 2 print '------' print LA.norm(self.data_mat[:, 0] - self.W * H_pre[:, 0], 'fro') ** 2 print LA.norm(self.data_mat[:, 0] - self.W * self.H[:, 0], 'fro') ** 2 print '------' print LA.norm(self.data_mat[:, 1] - self.W * H_pre[:, 1], 'fro') ** 2 print LA.norm(self.data_mat[:, 1] - self.W * self.H[:, 1], 'fro') ** 2 raise ValueError('Error: j = ' + str(j)) #print self.H[:, j] ''' if verbose: print 'KM: iter = ' + str(self.get_iter_num()) + ', after update H -' + \ ', nmf cost = ' + str(self.get_nmf_cost(self.W, self.H)) #c_cost = self.get_nmf_cost(self.W, self.H) #if c_cost > p_cost: # print self.H # raise ValueError('Error') # update W if self.flag == 0: # use the LS or K-means way to update W (centroids) for k in range(ha): if LA.norm(self.H[k, :], 2) == 0: # if no data points belongs to cluster k self.W[:, k].fill(0) else: self.W[:, k] = self.data_mat * self.H[k, :].transpose() / ( LA.norm(self.H[k, :], 2)**2) else: # use the gradient descent to update W Hessian = self.H * self.H.transpose() #c = 0.5 * LA.norm(Hessian, 'fro') egenvals, _ = LA.eigh(Hessian) c = 0.51 * np.max(egenvals) grad_W_pre = self.W * Hessian - self.data_mat * self.H.transpose() self.W = np.maximum(0, self.W - grad_W_pre / c) if verbose: print 'KM: iter = ' + str(self.get_iter_num()) + ', after update W -' + \ ', nmf cost = ' + str(self.get_nmf_cost(self.W, self.H)) def solve(self): start_time = time.time() self.set_tol(1e-5) end_time = time.time() self.time_used += end_time - start_time #cost = self.get_nmf_cost(self.W, self.H) cost = self.get_nmf_cost(self.W, self.H) #self.converge.add_obj_value(cost) self.converge.add_obj_value(cost) self.converge.add_prim_value('W', self.W) self.converge.add_prim_value('H', self.H) print self.H[:, 0] acc_km = [] # record the clustering accuracy for each SNCP iteration time_km = [] # record the time used after each SNCP iteration # calculate the clustering accurary pre_labels = np.argmax(np.asarray(self.H), 0) if self.labels is None: raise ValueError('Error: no labels!') acc = calculate_accuracy(pre_labels, self.labels) acc_km.append(acc) time_km.append(self.time_used) print 'Start to solve the problem by KM ----------' while not self.converge.d(): # update the variable W , H start_time = time.time() self.update_scheme(verbose=False) end_time = time.time() self.time_used += end_time - start_time time_km.append(self.time_used) print 'time used: ' + str(self.time_used) # calculate the clustering accurary pre_labels = np.argmax(np.asarray(self.H), 0) if self.labels is None: raise ValueError('Error: no labels!') acc = calculate_accuracy(pre_labels, self.labels) acc_km.append(acc) # store the newly obtained values for convergence analysis self.converge.add_prim_value('W', self.W) self.converge.add_prim_value('H', self.H) # store the obj_val cost = self.get_nmf_cost(self.W, self.H) self.converge.add_obj_value(cost) print 'onmf_KM: iter = ' + str( self.get_iter_num()) + ', nmf_cost = ' + str(cost) print 'HTH:' print self.H * self.H.transpose() # show the number of inner iterations self.converge.save_data(time_km, self.output_dir, 'time_km.csv') self.converge.save_data(acc_km, self.output_dir, 'acc_km.csv') print 'Stop the solve the problem ---------' self.converge_analysis() ''' return the solution W, H ''' def get_solution(self): return self.W, self.H ''' return the optimal obj val ''' def get_opt_obj_and_fea(self): return self.get_nmf_cost(self.W, self.H), None ''' return the iteration number and time used ''' def get_iter_and_time(self): return self.get_iter_num(), self.time_used ''' simulation result analysis (convergence plot) ''' def converge_analysis(self): # get the dirname to store the result: data file and figure #dir_name = path.join(self.res_dir, 'onmf', 'penalty', 'inner<1e-3', 'rank' + str(self.rank), 'SNR-3', 'seed' + str(self.seed_num)) dir_name = self.output_dir print 'Start to plot and store the obj convergence ------' self.converge.plot_convergence_obj(dir_name) print 'Start to plot and store the primal change ------' self.converge.plot_convergence_prim_var(dir_name) print 'Start to plot and store the fea condition change ------' self.converge.plot_convergence_fea_condition(dir_name) print 'Start to store the obj values and the factors' self.converge.store_obj_val(dir_name) self.converge.store_prim_val( -1, dir_name) # store the last element of primal variabl
class SNCP2_Solver(object): def __init__(self, data_manager=None, res_dir=None, rank=4, seed_num=1, mul=0, nu=0): if data_manager is None or res_dir is None: raise ValueError('Error: some inputs are missing!') self.data_manager = data_manager self.W, self.H = self.data_manager.gen_inits_WH(init='random', seed=seed_num, H_ortho=True) self.data_mat = self.data_manager.get_data_mat() self.mul = mul self.nu = nu self.rank = rank self.res_dir = res_dir self.seed_num = seed_num self.converge = Convergence(res_dir) self.true_labels = self.data_manager.get_labels() self.n_factor = LA.norm(self.data_mat, 'fro')**2 self.W_bound = False # flag to indicate whether to constrain W by upper bound and lower bound self.W_step = 0.51 self.H_step = 0.51 self.time_used = 0 # record the time elapsed when running the simulation start_time = time.time() self.initialize_penalty_para() end_time = time.time() self.time_used += end_time - start_time self.set_tol(1e-3) self.set_max_iters(400) W_bound = 'W_bound' if self.W_bound else 'W_nobound' self.output_dir = path.join(self.res_dir, 'onmf', 'sncp2_W1H1', \ W_bound + '_' + 'epsilon' + str(self.inner_tol) + '&gamma' + str(self.gamma) + '&mul' + str(self.mul) + '&nu' + str(self.nu), \ 'rank' + str(self.rank), self.data_manager.get_data_name(), 'seed' + str(self.seed_num)) # we construct a result manager to manage and save the result res_dir1 = path.join(res_dir, 'onmf', 'sncp2_new', self.data_manager.get_data_name(), 'cls' + str(rank), W_bound + 'W'+ str(self.W_step) + 'H' + str(self.H_step), \ 'inner' + str(self.inner_tol) + '&gamma' + str(self.gamma) + '&mul' + str(self.mul) + '&nu' + str(self.nu), 'seed' + str(self.seed_num)) self.res_manager = ClusterONMFManager( root_dir=res_dir1, save_pdv=False ) # get an instance of ClusterONMFManager to manage the generated result # initialize some variables to store info self.acc_iter = [] # record the clustering accuracy for each iteration self.time_iter = [] # record the time for each iteration self.nmf_cost_iter = [] # record the nmf cost after each iteration self.pobj_iter = [ ] # record the penalized objective value after each iteration self.obj_iter = [] # record the objective value for each iteration def set_max_iters(self, num): self.converge.set_max_iters(num) def set_tol(self, tol): self.converge.set_tolerance(tol) def initialize_penalty_para(self): self.rho = 1e-8 self.gamma = 1.1 self.inner_tol = 3e-3 (ha, hb) = self.H.shape self.I_ha = np.asmatrix(np.eye(ha)) self.B = np.zeros_like(self.H) self.all_1_mat = np.asmatrix(np.ones((ha, hb))) self.max_val = np.max(self.data_mat) self.min_val = np.min(self.data_mat) def get_nmf_cost(self, W, H): res = LA.norm(self.data_mat - np.asmatrix(W) * np.asmatrix(H), 'fro')**2 / self.n_factor return res def get_obj_val(self, W, H): res = LA.norm(self.data_mat - np.asmatrix(W) * np.asmatrix(H), 'fro')**2 / self.n_factor \ + 0.5 * self.nu * LA.norm(H, 'fro') ** 2 + 0.5 * self.mul * LA.norm(W, 'fro') ** 2 return res def get_penalized_obj(self, W, H): ''' objective function ||X - WH||_{F}^{2} + self.rho * sum{||hj||_{1} - ||hj||_{infty}} + 0.5 * ||H||_{F}^2 ''' (ha, hb) = H.shape tmp = 0 for k in range(hb): tmp = tmp + (LA.norm(H[:, k], 1) - LA.norm(H[:, k], np.inf)) return LA.norm(self.data_mat - W * H, 'fro') ** 2 / self.n_factor + self.rho * tmp \ + 0.5 * self.nu * LA.norm(H, 'fro') ** 2 + 0.5 * self.mul * LA.norm(W, 'fro') def get_onmf_cost(self, W, H, nu=0, mul=0): ''' This function returns the approximation error of ONMF based on current W and H Args: W (numpy array or mat): the factor W H (numpy array or mat): the factor H nu (float): the penalty parameter Returns: the cost ''' res = LA.norm(self.data_mat - W * H, 'fro')**2 / self.n_factor \ + 0.5 * nu * LA.norm(H, 'fro')** 2 \ + 0.5 * mul * LA.norm(W, 'fro') ** 2 return res def get_sncp_cost(self, W, H, nu=0, mul=0, rho=0): ''' This function returns the cost of the penalized subproblem when using SNCP Args: W (numpy array or mat): the factor W H (numpy array or mat): the factor H nu (float): the parameter nu * ||H||_F^2 mul (float): the parameter mul * ||W||_F^2 rho (float): the penalty parameter rho * \sum_j (||hj||_1 - ||hj||_{\infty}) Returns: the cost ''' (ha, hb) = H.shape tmp = 0 for k in range(hb): tmp = tmp + (LA.norm(H[:, k], 1) - LA.norm(H[:, k], np.inf)) return LA.norm(self.data_mat - W * H, 'fro') ** 2 / self.n_factor + rho * tmp \ + 0.5 * nu * LA.norm(H, 'fro') ** 2 + 0.5 * mul * LA.norm(W, 'fro') def get_iter_num(self): return self.converge.len() def update_prim_var_by_PALM0(self, k, W_init=None, H_init=None, max_iter=1000, tol=1e-1, verbose=False): ''' This function alternatively updates the primal variables in a Gauss-Seidel fasion. The update of H is performed using the proximal gradient method The update of W is performed using the proximal subgradient method Input: k ------ the outer iteration number W_init ------ the initialization for W H_init ------ the initialization for H max_iter ------ the max number of iterations for PALM tol ------ the tolerance for stopping PALM verbose ------ flag to control output debug info ''' if W_init is None or H_init is None: raise ValueError( 'Error: inner iterations by PLAM are lack of initializations!') start_time = time.time() # record the start time H_j_pre, W_j_pre = np.asmatrix(np.copy(H_init)), np.asmatrix( np.copy(W_init)) (ha, hb) = H_j_pre.shape end_time = time.time() self.time_used += end_time - start_time for j in range(max_iter): # update H and W by proximal gradient method respectively start_time = time.time() self.B.fill(0) self.B[H_j_pre.argmax(0), np.arange(hb)] = 1 Hessian = 2 * W_j_pre.transpose( ) * W_j_pre / self.n_factor + self.nu * self.I_ha t = self.H_step * LA.eigvalsh(Hessian)[ha - 1] grad_H_pre = Hessian * H_j_pre - 2 * W_j_pre.transpose() * self.data_mat / self.n_factor + \ self.rho * (self.all_1_mat - self.B) H_j_cur = np.maximum(0, H_j_pre - grad_H_pre / t) Hessian = 2 * H_j_cur * H_j_cur.transpose( ) / self.n_factor + self.mul * self.I_ha c = self.W_step * LA.eigvalsh(Hessian)[ha - 1] grad_W_pre = W_j_pre * Hessian - 2 * self.data_mat * H_j_cur.transpose( ) / self.n_factor if self.W_bound: W_j_cur = np.minimum(self.max_val, np.maximum(0, W_j_pre - grad_W_pre / c)) else: W_j_cur = np.maximum(0, W_j_pre - grad_W_pre / c) if verbose: obj = self.get_obj_val(W_j_cur, H_j_cur) pobj = self.get_penalized_obj(W_j_cur, H_j_cur) # store the info # calculate the clustering accurary pre_labels = np.argmax(np.asarray(H_j_cur), 0) if self.labels is None: raise ValueError('Error: no labels!') acc = calculate_accuracy(pre_labels, self.labels) self.acc_iter.append(acc) self.obj_iter.append(obj) self.pobj_iter.append(pobj) cost = self.get_nmf_cost(W_j_cur, H_j_cur) self.nmf_cost_iter.append(cost) onmf_cost = self.get_onmf_cost(W_j_cur, H_j_cur, self.nu, self.mul) sncp_cost = self.get_sncp_cost(W_j_cur, H_j_cur, self.nu, self.mul, self.rho) self.res_manager.add_cost_value('onmf_cost_palm', onmf_cost) # store obj val self.res_manager.add_cost_value('palm_cost', sncp_cost) nmf_cost = self.get_onmf_cost(W_j_cur, H_j_cur, 0, 0) self.res_manager.add_cost_value('nmf_cost_palm', nmf_cost) #check the convergence H_j_change = LA.norm(H_j_cur - H_j_pre, 'fro') / LA.norm( H_j_pre, 'fro') W_j_change = LA.norm(W_j_cur - W_j_pre, 'fro') / LA.norm( W_j_pre, 'fro') #update the pres H_j_pre = np.asmatrix(np.copy(H_j_cur)) W_j_pre = np.asmatrix(np.copy(W_j_cur)) end_time = time.time() self.time_used += end_time - start_time self.time_iter.append(self.time_used) #self.res_manager.push_time(self.time_used) #self.res_manager.push_iters(self.rho, j+1) # save the info if H_j_change + W_j_change < tol: self.res_manager.push_iters(self.rho, j + 1) break return (W_j_cur, H_j_cur, j + 1) def update_prim_var_by_PALM1(self, k, W_init=None, H_init=None, max_iter=1000, tol=1e-1, verbose=False): ''' This function alternatively updates the primal variables in a Gauss-Seidel fasion. Each update is performed using the proximal gradient method Input: k ------ the outer iteration number W_init ------ the initialization for W H_init ------ the initialization for H max_iter ------ the max number of iterations for PALM tol ------ the tolerance for stopping PALM verbose ------ flag to control output debug info ''' if W_init is None or H_init is None: raise ValueError( 'Error: inner iterations by PLAM are lack of initializations!') start_time = time.time() # record the start time H_j_pre, W_j_pre, H_j_cur, W_j_cur = H_init, W_init, H_init, W_init (ha, hb) = H_j_pre.shape end_time = time.time() self.time_used += end_time - start_time for j in range(max_iter): # update H and W by proximal gradient method respectively #if verbose: # print 'PALM1: inner iter = ' + str(j) + ', before H, obj_val = ' + \ # str(self.get_obj_val(W_j_pre, H_j_pre)) + ', penalized_obj = ' + str(self.get_penalized_obj(W_j_pre, H_j_pre)) start_time = time.time() # keep the infinity norm as a non-smooth part Hessian = 2 * W_j_pre.transpose( ) * W_j_pre / self.n_factor + self.nu * self.I_ha t = self.H_step * LA.eigvalsh(Hessian)[ha - 1] grad_H_pre = Hessian * H_j_pre - 2 * W_j_pre.transpose( ) * self.data_mat / self.n_factor + self.rho * self.all_1_mat H_j_cur = H_j_pre - grad_H_pre / t self.B.fill(0) self.B[H_j_cur.argmax(0), np.arange(hb)] = 1 H_j_cur += (self.rho / t) * self.B H_j_cur = np.maximum(H_j_cur, 0) Hessian = 2 * H_j_cur * H_j_cur.transpose( ) / self.n_factor + self.mul * self.I_ha c = self.W_step * LA.eigvalsh(Hessian)[ha - 1] grad_W_pre = W_j_pre * Hessian - 2 * self.data_mat * H_j_cur.transpose( ) / self.n_factor if self.W_bound: W_j_cur = np.minimum(self.max_val, np.maximum(0, W_j_pre - grad_W_pre / c)) else: W_j_cur = np.maximum(0, W_j_pre - grad_W_pre / c) if verbose: obj = self.get_obj_val(W_j_cur, H_j_cur) pobj = self.get_penalized_obj(W_j_cur, H_j_cur) # calculate the clustering accurary pre_labels = np.argmax(np.asarray(H_j_cur), 0) if self.true_labels is None: raise ValueError('Error: no labels!') acc = calculate_accuracy(pre_labels, self.true_labels) self.acc_iter.append(acc) self.obj_iter.append(obj) self.pobj_iter.append(pobj) cost = self.get_nmf_cost(W_j_cur, H_j_cur) self.nmf_cost_iter.append(cost) onmf_cost = self.get_onmf_cost(W_j_cur, H_j_cur, self.nu, self.mul) sncp_cost = self.get_sncp_cost(W_j_cur, H_j_cur, self.nu, self.mul, self.rho) self.res_manager.add_cost_value('onmf_cost_palm', onmf_cost) # store obj val self.res_manager.add_cost_value('palm_cost', sncp_cost) nmf_cost = self.get_onmf_cost(W_j_cur, H_j_cur, 0, 0) self.res_manager.add_cost_value('nmf_cost_palm', nmf_cost) # check the convergence H_j_change = LA.norm(H_j_cur - H_j_pre, 'fro') / LA.norm( H_j_pre, 'fro') W_j_change = LA.norm(W_j_cur - W_j_pre, 'fro') / LA.norm( W_j_pre, 'fro') # update the pres H_j_pre = np.asmatrix(np.copy(H_j_cur)) W_j_pre = np.asmatrix(np.copy(W_j_cur)) end_time = time.time() self.time_used += end_time - start_time self.time_iter.append(self.time_used) #self.res_manager.push_time(self.time_used) #self.res_manager.push_iters(self.rho, j+1) if H_j_change + W_j_change < tol: self.res_manager.push_iters(self.rho, j + 1) break return (W_j_cur, H_j_cur, j + 1) def update_scheme(self): ''' The updating rules for primal variables, W, H and the penalty parameter rho use proximal gradient method to update each varialbe once for each iteration ''' # update # (self.W, self.H, inner_iter_num) = self.update_prim_var_by_PALM0(self.get_iter_num(), self.W, self.H, 3000, self.inner_tol, verbose = False) (self.W, self.H, inner_iter_num) = self.update_prim_var_by_PALM1(self.get_iter_num(), self.W, self.H, 3000, self.inner_tol, verbose=False) # show the feasibility satisfaction level HH^{T} - I (ha, hb) = self.H.shape H_norm = np.asmatrix( np.diag(np.diag(self.H * self.H.transpose())**(-0.5))) * self.H fea = LA.norm(H_norm * H_norm.transpose() - np.asmatrix(np.eye(ha)), 'fro') / (ha * ha) start_time = time.time() #if self.get_iter_num() > 0 and fea > 1e-10: self.rho = np.minimum(self.rho * self.gamma, 1e10) print self.rho end_time = time.time() self.time_used += end_time - start_time return inner_iter_num def solve(self): ''' problem formulation min ||X - WH||_{F}^{2} + rho * sum{||hj||_{1} - ||hj||_{infty} + 0.5 *nu * ||H||_F^2 * 0.5 * mul * ||W||_F^2 ''' obj = self.get_obj_val(self.W, self.H) p_obj = self.get_penalized_obj(self.W, self.H) print 'The initial error: iter = ' + str(self.get_iter_num( )) + ', obj_val =' + str(obj) + ', penalized_obj =' + str(p_obj) #self.converge.add_obj_value(cost) self.converge.add_obj_value(obj) self.converge.add_prim_value('W', self.W) self.converge.add_prim_value('H', self.H) print self.H[:, 0] inner_iter_nums = [] # record the inner iterations number acc_sncp = [] # record the clustering accuracy for each SNCP iteration time_sncp = [] # record the time used after each SNCP iteration nmf_cost_sncp = [] # record the nmf cost after each SNCP iteration pobj_sncp = [ ] # record the penalized objective value after each iteration cost = self.get_nmf_cost(self.W, self.H) nmf_cost_sncp.append(cost) pobj_sncp.append(p_obj) self.pobj_iter.append(p_obj) self.nmf_cost_iter.append(cost) self.obj_iter.append(obj) # calculate the clustering accurary pre_labels = np.argmax(np.asarray(self.H), 0) if self.true_labels is None: raise ValueError('Error: no labels!') print len(self.true_labels) acc = calculate_accuracy(pre_labels, self.true_labels) acc_sncp.append(acc) self.acc_iter.append(acc) time_sncp.append(self.time_used) self.time_iter.append(self.time_used) fea = 100 self.res_manager.push_W(self.W) # store W self.res_manager.push_H(self.H) # store H self.res_manager.push_H_norm_ortho() # store feasibility nmf_cost = self.get_onmf_cost(self.W, self.H, 0, 0) onmf_cost = self.get_onmf_cost(self.W, self.H, self.nu, self.mul) sncp_cost = self.get_sncp_cost(self.W, self.H, self.nu, self.mul, self.rho) self.res_manager.add_cost_value('onmf_cost_sncp', onmf_cost) # store obj val self.res_manager.add_cost_value('sncp_cost', sncp_cost) self.res_manager.add_cost_value('onmf_cost_palm', onmf_cost) # store obj val self.res_manager.add_cost_value('palm_cost', sncp_cost) self.res_manager.add_cost_value('nmf_cost_sncp', nmf_cost) self.res_manager.add_cost_value('nmf_cost_palm', nmf_cost) cls_assign = self.res_manager.calculate_cluster_quality( self.true_labels) # calculate and store clustering quality self.res_manager.push_time(self.time_used) print 'Start to solve the problem by SNCP2 ----------' while not self.converge.d() or fea > 1e-10: # update the variable W , H num = self.update_scheme() inner_iter_nums.append(num) time_sncp.append(self.time_used) print 'time used: ' + str( self.time_used) + ', inner_num: ' + str(num) # calculate the clustering accurary pre_labels = np.argmax(np.asarray(self.H), 0) if self.true_labels is None: raise ValueError('Error: no labels!') acc = calculate_accuracy(pre_labels, self.true_labels) acc_sncp.append(acc) # store the newly obtained values for convergence analysis self.converge.add_prim_value('W', self.W) self.converge.add_prim_value('H', self.H) # store the nmf approximation error value obj = self.get_obj_val(self.W, self.H) p_obj = self.get_penalized_obj(self.W, self.H) nmf_cost_sncp.append(self.get_nmf_cost(self.W, self.H)) self.converge.add_obj_value(obj) pobj_sncp.append(p_obj) print 'onmf_SNCP2: iter = ' + str(self.get_iter_num( )) + ', obj_val = ' + str(obj) + ' penalized_obj = ' + str(p_obj) # store the satisfaction of feasible conditions (ha, hb) = self.H.shape H_norm = np.asmatrix( np.diag(np.diag(self.H * self.H.transpose())**(-0.5))) * self.H fea = LA.norm( H_norm * H_norm.transpose() - np.asmatrix(np.eye(ha)), 'fro') / (ha * ha) #print 'normalized orthogonality: ' + str(fea) self.converge.add_fea_condition_value('HTH_I', fea) # store the generated results by result manager self.res_manager.push_W(self.W) # store W self.res_manager.push_H(self.H) # store H self.res_manager.push_H_norm_ortho() # store feasibility self.res_manager.push_W_norm_residual() self.res_manager.push_H_norm_residual() nmf_cost = self.get_onmf_cost(self.W, self.H, 0, 0) onmf_cost = self.get_onmf_cost(self.W, self.H, self.nu, self.mul) sncp_cost = self.get_sncp_cost(self.W, self.H, self.nu, self.mul, self.rho) self.res_manager.add_cost_value('onmf_cost_sncp', onmf_cost) # store obj val self.res_manager.add_cost_value('sncp_cost', sncp_cost) self.res_manager.add_cost_value('onmf_cost_palm', onmf_cost) # store obj val self.res_manager.add_cost_value('palm_cost', sncp_cost) self.res_manager.add_cost_value('nmf_cost_sncp', nmf_cost) self.res_manager.add_cost_value('nmf_cost_palm', nmf_cost) cls_assign = self.res_manager.calculate_cluster_quality( self.true_labels) # calculate and store clustering quality self.res_manager.push_time(self.time_used) print 'HTH:' print self.H * self.H.transpose() print 'the L2-norm of columns of H:' print LA.norm(self.H, axis=0) # show the number of inner iterations self.converge.save_data(inner_iter_nums, self.output_dir, 'inner_nums.csv') #self.converge.save_data(time_sncp, self.output_dir, 'time_sncp.csv') self.converge.save_data(acc_sncp, self.output_dir, 'acc_sncp.csv') self.converge.save_data(nmf_cost_sncp, self.output_dir, 'nmf_cost_sncp.csv') self.converge.save_data(pobj_sncp, self.output_dir, 'pobj_sncp.csv') self.converge.save_data(self.obj_iter, self.output_dir, 'obj_iters.csv') self.converge.save_data(self.acc_iter, self.output_dir, 'acc_iters.csv') self.converge.save_data(self.nmf_cost_iter, self.output_dir, 'nmf_cost_iters.csv') self.converge.save_data(self.pobj_iter, self.output_dir, 'pobj_iters.csv') self.converge.save_data(self.time_iter, self.output_dir, 'time_iters.csv') print 'Stop the solve the problem ---------' self.converge_analysis() self.res_manager.write_to_csv( ) # store the generated results to csv files ''' return the solution W, H ''' def get_solution(self): return self.W, self.H ''' return the optimal obj val ''' def get_opt_obj_and_fea(self): return self.get_nmf_cost( self.W, self.H), self.converge.get_last_fea_condition_value('HTH_I') ''' return the iteration number and time used ''' def get_iter_and_time(self): return self.get_iter_num(), self.time_used def get_time(self): return self.time_used ''' return the cluster assignment from H ''' def get_cls_assignment_from_H(self): labels = np.argmax(np.asarray(self.H), 0) if len(labels) != self.data_mat.shape[1]: raise ValueError( 'Error: the size of data samples must = the length of labels!') return labels ''' simulation result analysis (convergence plot) ''' def converge_analysis(self): # get the dirname to store the result: data file and figure #dir_name = path.join(self.res_dir, 'onmf', 'penalty', 'inner<1e-3', 'rank' + str(self.rank), 'SNR-3', 'seed' + str(self.seed_num)) dir_name = self.output_dir print 'Start to plot and store the obj convergence ------' self.converge.plot_convergence_obj(dir_name) print 'Start to plot and store the primal change ------' self.converge.plot_convergence_prim_var(dir_name) print 'Start to plot and store the fea condition change ------' self.converge.plot_convergence_fea_condition(dir_name) print 'Start to store the obj values and the factors' self.converge.store_obj_val(dir_name) self.converge.store_prim_val( -1, dir_name) # store the last element of primal variabl