def update_feedback_processing_object(self, X_obs): if self.FP is None: self.FP = FeedbackProcessing(self.D, self.m, self.original_bounds, self.alpha_grid_distribution, self.TGN_speed) self.FP.initialize_data(X_obs) else: self.FP.update_data(X_obs)
def __init__(self, PPBO_settings): """ Basic settings """ self.D = PPBO_settings.D #Problem dimension self.bounds = PPBO_settings.original_bounds #Boundaries of each variables as a sequence of tuplets self.alpha_grid_distribution = PPBO_settings.alpha_grid_distribution self.user_feedback_grid_size = PPBO_settings.user_feedback_grid_size #How many possible points in the user feedback grid? self.n_irfs_periods = 20 #This must be same as in a model.mod file self.FP = FeedbackProcessing(self.D, self.user_feedback_grid_size, self.bounds, self.alpha_grid_distribution, PPBO_settings.TGN_speed) self.current_xi = None self.current_x = None self.current_xi_grid = None self.dsge_results = None self.user_feedback = None #variable to store user feedback self.user_feedback_was_given = False self.popup_slider_has_been_called = False self.results = pd.DataFrame( columns=(['alpha_xi_x' + str(i) for i in range(1, self.D + 1)] + ['xi' + str(i) for i in range(1, self.D + 1)] + ['alpha_star']), dtype=np.float64 ) #The output of the session is a dataframe containing user feedback self.engine = 'OCTAVE' ''' Prepare model files to temp ''' self.path_to_dsge = os.getcwd() + '/dsge' #CHECK THAT THIS IS CORRECT if os.path.exists(self.path_to_dsge + '/temp'): shutil.rmtree(self.path_to_dsge + '/temp') os.mkdir(self.path_to_dsge + '/temp') for i in range(self.user_feedback_grid_size): shutil.copy2('dsge/US_FU19_rep.mod', 'dsge/temp/US_FU19_rep_' + str(i) + '.mod') shutil.copy2('dsge/prior_function_US_FU19.m', 'dsge/temp/prior_function_US_FU19.m')
def __init__(self,PPBO_settings): """ Basic settings """ self.D = PPBO_settings.D #Problem dimension self.bounds = PPBO_settings.original_bounds #Boundaries of each variables as a sequence of tuplets self.alpha_grid_distribution = PPBO_settings.alpha_grid_distribution self.user_feedback_grid_size = 100 #How many possible points in the user feedback grid? self.FP = FeedbackProcessing(self.D, self.user_feedback_grid_size,self.bounds,self.alpha_grid_distribution,PPBO_settings.TGN_speed) self.current_xi = None self.current_x = None self.current_xi_grid = None self.user_feedback = None #variable to store user feedback self.user_feedback_was_given = False self.popup_configuration_movie_has_been_called = False self.results = pd.DataFrame(columns=(['alpha_xi_x' + str(i) for i in range(1,6+1)] + ['xi' + str(i) for i in range(1,6+1)] + ['alpha_star']),dtype=np.float64) #The output of the session is a dataframe containing user feedback
class GPModel: """ Class for GP utility function model. Some methods are not inherited but composited from Feedback_Processing class """ def __init__(self, PPBO_settings): """ Initializes the GP_Model object """ self.COVARIANCE_SHRINKAGE = 7e-6 #An amount of shrinkage applied to Sigma. DEFAULT: 1e-6 (1e-5 - 1e-6) #Low value is better but is numerically more unstable #Note! Higher value increases difference to random Fourier features approximation of f self.verbose = PPBO_settings.verbose self.FP = None self.D = PPBO_settings.D #Problem dimension self.original_bounds = PPBO_settings.original_bounds #Boundaries of each variables as a sequence of tuplets self.bounds = ((0,1),)*self.D self.X = None #Design Matrix self.N = None #Number of observations including pseudo-observations self.m = PPBO_settings.n_pseudoobservations #How many pseudo-observations per one observation? self.obs_indices = None #Locations of true observations self.pseudobs_indices = None #Locations of pseudo-observations self.latest_obs_indices = None #Location of latest true observation given all observations from 0 to N self.alpha_grid_distribution = PPBO_settings.alpha_grid_distribution #equispaced, Cauchy or TGN self.TGN_speed = PPBO_settings.TGN_speed self.n_gausshermite_sample_points = PPBO_settings.n_gausshermite_sample_points self.xi_acquisition_function = PPBO_settings.xi_acquisition_function self.kernel = eval(PPBO_settings.kernel) #Kernel type self.theta_initial = PPBO_settings.theta_initial self.theta = None #Most optimized theta self.Sigma = None self.Sigma_inv = None self.Lambda_MAP = None self.posterior_covariance = None self.fMAP = None self.fMAP_finding_trials = 1 self.fMAP_optimizer = PPBO_settings.fMAP_optimizer self.fMAP_random_initial_vector = True self.mustar_finding_trials = PPBO_settings.mustar_finding_trials self.mustar_previous_iteration = 0 self.mustar = None #Global maximum of (predictive mean) utility function self.xstar = None #Global maximizer of (predictive mean) utility function self.xstars_local = None #Local maximizers of utility function observed during the optimization self.initialization_running = True #Less intensive computations during initialization (if skip_... = True) self.last_iteration = False #Super intensive computations at the last iteration self.skip_computations_during_initialization = PPBO_settings.skip_computations_during_initialization self.skip_xstaroptimization_during_initialization = PPBO_settings.skip_xstaroptimization_during_initialization ''' --- Wrapper functions --- ''' def update_feedback_processing_object(self,X_obs): if self.FP is None: self.FP = FeedbackProcessing(self.D, self.m,self.original_bounds,self.alpha_grid_distribution,self.TGN_speed) self.FP.initialize_data(X_obs) else: self.FP.update_data(X_obs) def update_data(self): self.X = self.FP.X self.N = self.FP.N self.obs_indices = self.FP.obs_indices self.pseudobs_indices = self.FP.pseudobs_indices self.latest_obs_indices = self.FP.latest_obs_indices def update_model(self,optimize_theta=False): ''' Note: Optimize_theta is still quite unstable, thus it's not recommended ''' if self.theta is None: self.set_theta() self.update_Sigma(self.theta) self.update_Sigma_inv(self.theta) if self.initialization_running and self.skip_computations_during_initialization: self.FP.alpha_grid_distribution = 'equispaced' #Always use 'equispaced' pseudo-observations in initialization self.update_fMAP(random_initial_vector=False,fmap_finding_trials=1,approx_optimization=True) elif self.last_iteration: self.update_fMAP(random_initial_vector=True,fmap_finding_trials=5) else: self.update_fMAP() if optimize_theta: self.optimize_theta() self.update_fMAP() self.update_Sigma(self.theta) self.update_Sigma_inv(self.theta) if self.verbose: print("Current theta is: " + str(self.theta) + ' (Acq. = ' +str(self.xi_acquisition_function)+')') if self.initialization_running and self.skip_computations_during_initialization: pass else: if self.verbose: print("Updating Lambda_MAP...") start = time.time() self.Lambda_MAP = self.create_Lambda(self.fMAP,self.theta[0]) if self.verbose: print("... this took " + str(time.time()-start) + " seconds.") if self.verbose: print("Updating posterior covariance...") start = time.time() try: self.posterior_covariance_inv = self.Sigma_inv - self.Lambda_MAP #self.Sigma_inv + self.Lambda_MAP self.posterior_covariance = pd_inverse(self.posterior_covariance_inv) except: print('---!!!--- Posterior covariance matrix is not PSD ---!!!---') pass if self.verbose: print("... this took " + str(time.time()-start) + " seconds.") if self.verbose: print("Computing mu_star and x_star ...") start = time.time() if self.initialization_running and self.skip_computations_during_initialization and not self.skip_xstaroptimization_during_initialization: self.xstar, self.mustar, self.xstars_local = self.mu_star(mustar_finding_trials=1) elif self.initialization_running and self.skip_xstaroptimization_during_initialization: pass elif self.last_iteration: self.xstar, self.mustar, self.xstars_local = self.mu_star(mustar_finding_trials=5) else: self.xstar, self.mustar, self.xstars_local = self.mu_star() if self.verbose: print("... this took " + str(time.time()-start) + " seconds.") ''' Auxiliary function ''' def turn_initialization_off(self): self.initialization_running = False self.FP.alpha_grid_distribution = self.alpha_grid_distribution def set_last_iteration(self): self.last_iteration = True def is_pseudobs(self,i): return self.FP.is_pseudobs(i) ''' --- Covariance matrix --- ''' def create_Gramian(self,X1,X2,kernel,*args): Sigma = kernel(X1,X2, *args) ''' REGULARIZATION OF THE COVARIANCE MATRIX''' Sigma = regularize_covariance(Sigma,self.COVARIANCE_SHRINKAGE) return Sigma def create_Gramian_nonsquare(self,X1,X2,kernel,*args): Sigma = kernel(X1,X2, *args) return Sigma def update_Sigma(self,theta): self.Sigma = self.create_Gramian(self.X,self.X,self.kernel,theta) #print("The condition number of the covariance matrix: " + str(np.linalg.cond(self.Sigma,p=np.inf))) #condition number of A = ||A||*||A^-1||, where the norm ||.|| is the maximum absolute row sum (since p =np.inf) def update_Sigma_inv(self,theta): self.Sigma_inv = pd_inverse(self.Sigma) def set_theta(self): self.theta = self.theta_initial if self.theta[1] is None: self.theta[1] = 1 if self.theta[2] is None: self.theta[2] = 0.01 if self.theta[0] is None: self.theta[0] = 0.01 ''' --- Functional T --- ''' ''' Auxiliary functions for computing transormations/vectroizations of Phi ''' def sum_Phi(self,i,order_of_derivative,f,sigma,sample_points=None, weights=None): ''' Auxiliary function that computes summation of Phi-function values i = index of x observation, i.e. some element of list 'obs_indices' order_of_derivative = how many this the c.d.f of the standard normal (Phi) is differentiated? f = vector of f-values ''' m = self.m #Delta_i_j = (f[i+j+1]-f[i])/(sigma_) Delta = f[i+1:i+m+1] Delta = (Delta - f[i])/sigma sum_=0 if order_of_derivative==0: for j in range(0,m): #sum_ = sum_ + integrate(lambda x: std_normal_cdf(Delta[j]+x)*std_normal_pdf(x), -np.inf, np.inf)[0] #Do integration by using Gaussian-Hermite quadrature: sum_ = sum_ + (1/np.sqrt(np.pi))*np.dot(weights,std_normal_cdf(Delta[j]-np.sqrt(2)*sample_points)) #Do to change of variables to get integteragl into the form int exp(x^2)*....dx. This is why np.sqrt(2) return(sum_) if order_of_derivative==1: for j in range(0,m): sum_ = sum_ + float(var2_normal_pdf(Delta[j])) return(sum_) if order_of_derivative==2: for j in range(0,m): sum_ = sum_ - 0.5*Delta[j]*float(var2_normal_pdf(Delta[j])) return(sum_) else: print("The derivatives of an order higher than 2 are not needed!") return None def sum_Phi_vec(self,order_of_derivative,f,sigma,over_all_indices=False): ''' Auxiliary function that create a vector of sum_Phi with elements as i = obs_indices[0],...,obs_indices[len(obs_indices)], if not over_all_indices i = 1,...,N if over_all_indices ''' sample_points, weights = np.polynomial.hermite.hermgauss(self.n_gausshermite_sample_points) #for cross-correlation integral if not over_all_indices: sum_Phi_vec_ = [self.sum_Phi(i,order_of_derivative,f,sigma,sample_points, weights) for i in self.obs_indices] return np.array(sum_Phi_vec_) else: sum_Phi_vec_ = list(chain.from_iterable([[self.sum_Phi(i,order_of_derivative,f,sigma,sample_points, weights)]*(self.m+1) for i in self.obs_indices])) #for z indices just replicate previous x value return np.array(sum_Phi_vec_) ''' Derivatives of the functional T of the order: 0,1,2 ''' def T(self,f,theta,Sigma_inv_=None): if Sigma_inv_ is None: Sigma_inv_=self.Sigma_inv sumPhi = self.sum_Phi_vec(0,f,theta[0]) T = -0.5*f.T.dot(Sigma_inv_).dot(f) - np.sum(sumPhi)/self.m return T def T_grad(self,f,theta,Sigma_inv_=None): if Sigma_inv_ is None: Sigma_inv_=self.Sigma_inv N = self.N m = self.m latest_obs_indices = self.latest_obs_indices beta = np.zeros((N,1)).reshape(N,) Phi_der_vec_ = np.array([float(var2_normal_pdf((f[j]-f[latest_obs_indices[j]])/theta[0])) for j in self.pseudobs_indices]) sum_Phi_der_vec_ = self.sum_Phi_vec(1,f,theta[0]) beta[self.obs_indices] = sum_Phi_der_vec_/(theta[0]*m) beta[self.pseudobs_indices] = -Phi_der_vec_/(theta[0]*m) T_grad = -Sigma_inv_.dot(f).reshape(N,) + beta.reshape(N,) return T_grad def T_hessian(self,f,theta,Sigma_inv_=None): if Sigma_inv_ is None: Sigma_inv_=self.Sigma_inv Lambda = self.create_Lambda(f,theta[0]) T_hessian = -Sigma_inv_ + Lambda return T_hessian def create_Lambda(self,f,sigma): N = self.N m = self.m sample_points, weights = np.polynomial.hermite.hermgauss(self.n_gausshermite_sample_points) ''' Diagonal matrix ''' Lambda_diagonal = [0]*N constant = 1/(m*(sigma**2)) for i in range(N): if not self.is_pseudobs(i): Lambda_diagonal[i] = -constant*self.sum_Phi(i,2,f,sigma) #Note that sum_Phi used so minus sign needed! else: latest_x_index = np.max([ind for ind in self.obs_indices if ind < i]) Delta_i = (f[i]-f[latest_x_index])/sigma Lambda_diagonal[i] = 0.5*constant*Delta_i*var2_normal_pdf(Delta_i) Lambda_diagonal = np.diag(Lambda_diagonal) ''' Upper triangular off-diagonal matrix''' Lambda_uppertri = np.zeros((N,N)) for row_ind in range(N): if not self.is_pseudobs(row_ind): for col_ind in range(row_ind+1,row_ind+m+1): if self.is_pseudobs(col_ind): Delta = (f[col_ind]-f[row_ind])/sigma Lambda_uppertri[row_ind,col_ind] = -0.5*constant*Delta*var2_normal_pdf(Delta) ''' Final Lambda is sum of diagonal + upper_tringular + lower_tringular ''' Lambda = Lambda_diagonal + Lambda_uppertri + Lambda_uppertri.T return Lambda ''' --- Evidence --- ''' def evidence(self,theta,f_initial): def prior(theta): ''' Hyperparameter priors. This stabilizes the optimization ''' #https://keisan.casio.com/exec/system/1180573226 priortheta0 = scipy.stats.beta.pdf(theta[0], 0.0005, 35) #loc around 0 priortheta1 = scipy.stats.beta.pdf(theta[1], 15, 35) #loc around 0.3 priortheta2 = scipy.stats.beta.pdf(theta[2], 3, 20) #loc around 0.1 return np.log(priortheta0)+np.log(priortheta1)+np.log(priortheta2) if self.verbose: print('---------- Iter results ----------------') Sigma_ = self.create_Gramian(self.X,self.X,self.kernel,theta) Sigma_inv_ = pd_inverse(Sigma_) f_initial = np.random.multivariate_normal([0]*self.N, self.Sigma).reshape(self.N,1) fMAP = scipy.optimize.minimize(lambda f: -self.T(f,theta,Sigma_inv_), f_initial, method='trust-exact', jac=lambda f: -self.T_grad(f,theta,Sigma_inv_), hess=lambda f: -self.T_hessian(f,theta,Sigma_inv_), options={'disp': False,'maxiter': 500}).x Lambda_MAP = self.create_Lambda(fMAP,theta[0]) ''' A straightforward (numerically unstable?) implementation ''' I = np.eye(self.N) matrix = I+Sigma_.dot(Lambda_MAP) (sign, logdet) = np.linalg.slogdet(matrix) determinant = sign*np.exp(logdet) #evidence = np.exp(self.T(fMAP,theta,Sigma_inv_))*np.power(determinant,-0.5) log_evidence = self.T(fMAP,theta,Sigma_inv_) - 0.5*np.log(determinant) if self.verbose: print('(scaled) Log-evidence: ' + str(log_evidence)) if self.verbose: print('Hyper-parameters: ' + str(theta)) value = log_evidence + prior(theta) if np.isnan(value) or not np.isfinite(value): if self.verbose: print('Nan log-evidence!') return -500 else: if self.verbose: print('(scaled) Log-evidence + Log-prior: ' + str(value)) return value ''' An implementation based on Cholesky-decomposition ''' #try: # posterior_covariance_inv = Sigma_inv_ - Lambda_MAP #Sigma_inv_ + Lambda_MAP # posterior_covariance = pd_inverse(posterior_covariance_inv) # L = np.linalg.cholesky(posterior_covariance) # log_determinant = 2*np.sum(np.log(np.diag(L))) # log_evidence = self.T(fMAP,theta,Sigma_inv_) - 0.5*log_determinant #determinant still too large, so resulting theta gives too regularized GP # print("Log-determinant: " + str(log_determinant)) # print('Hyper-parameters: ' + str(theta)) # return log_evidence #except: # print('Theta = ' +str(theta) + ' gave a non-PSD covariance matrix.') # return -1e+4 ''' --- Optimizations --- ''' def update_fMAP(self,random_initial_vector=None,fmap_finding_trials=None,approx_optimization=False): ''' Finds MAP estimate and updates it ''' if fmap_finding_trials is None: fmap_finding_trials = self.fMAP_finding_trials if random_initial_vector is None: random_initial_vector = self.fMAP_random_initial_vector ''' Optimizer: choose second-order either trust-krylov or trust-exact, but trust-exact does not work with pypet multiprocessing! ''' if self.fMAP_optimizer=='trust-krylov': verbose = False else: verbose=self.verbose if approx_optimization: options = {'disp': verbose,'gtol': 100} else: options = {'disp': verbose} if self.verbose: print("MAP-estimation begins...") start = time.time() min_ = 10**24 for i in range(0,fmap_finding_trials): #How many times mu_star is tried to find? if self.fMAP is None: f_initial = np.random.multivariate_normal([0]*self.N, self.Sigma).reshape(self.N,1) elif len(self.fMAP) < self.N and not random_initial_vector: #Last iteration map estimate is of course a vector of shorter length. Hence add enough constant (=mean of fMAP) to the tail of the map estimate. fMAP = np.insert(self.fMAP,len(self.fMAP),[np.mean(self.fMAP)]*(self.N-len(self.fMAP))) f_initial = fMAP elif len(self.fMAP) == self.N and not random_initial_vector: f_initial = self.fMAP else: f_initial = np.random.multivariate_normal([0]*self.N, self.Sigma).reshape(self.N,1) res = scipy.optimize.minimize(lambda f: -self.T(f,self.theta), f_initial, method=self.fMAP_optimizer, jac=lambda f: -self.T_grad(f,self.theta), hess=lambda f: -self.T_hessian(f,self.theta), options=options) if self.verbose: print('... this took ' + str(time.time()-start) + ' seconds.') if res.fun < min_: min_ = res.fun bestfMAP = res.x self.fMAP = bestfMAP def optimize_theta(self): ''' Optimizes all hyper-parameters (including noise) by maximizing the evidence ''' if self.verbose: print("Hyperparameter optimization begins...") start = time.time() #BayesianOptimization #Higher lengthscale generates more accurate GP mean (and location of maximizer of mean) #However, higher lengthscale also generates less accurate argmax distribution (argmax samples are widepread) ''' Bounds for hyperparameters: When COVARIANCE_SHRINKAGE = 1e-6''' bounds = [{'name': 'sigma', 'type': 'continuous', 'domain': (0.0001,0.1)}, #Too low noise gives non-PSD covariance matrix {'name': 'leghtscale', 'type': 'continuous', 'domain': (0.05,0.9)}, #Theoretical l max: 4*np.sqrt(self.D) {'name': 'sigma_l', 'type': 'continuous', 'domain': (0.01,0.5)}] # since f is a utility function this parameter make no much sense. BO = BayesianOptimization(lambda theta: -self.evidence(theta[0],self.fMAP), #theta[0] since need to unnest list one level domain=bounds, optimize_restarts=3, normalize_Y=True, initial_design_numdata=20) BO.run_optimization(max_iter = 50) if self.verbose: print('Optimization of hyperparameters took ' + str(time.time()-start) + ' seconds.') self.theta = BO.x_opt if self.verbose: print("The optimized theta is "+ str(self.theta)) def mu_star(self,mustar_finding_trials=None): ''' Function finds the optimal predictive mean and the maximizer x''' if mustar_finding_trials is None: mustar_finding_trials = self.mustar_finding_trials #Global seach with constraints (DIFFERENTIAL EVOLUTION OPTIMIZATION) bounds = self.bounds for i in range(0,mustar_finding_trials): #How many times mu_star is tried to find? res = scipy.optimize.differential_evolution(self.mu_pred_neq, bounds,updating='immediate', disp=False,maxiter=2000) #print(res.x) #to monitor how stable are results if i==0: xstar = res.x min_ = res.fun xstars_local = res.x xstars_local.shape = (1,self.D) else: if all([bool(np.linalg.norm(x-res.x) > 1e-1) for x in xstars_local]): xstars_local = np.vstack([xstars_local,res.x]) if res.fun < min_: min_ = res.fun xstar = res.x xstar.shape = (self.D,) mustar = self.mu_pred(xstar) return xstar,mustar,xstars_local ''' --- GP predictions --- ''' def mu_Sigma_pred(self,X_pred): ''' Predictive posterior means and covariances ''' #mean k_pred = self.create_Gramian_nonsquare(self.X,X_pred,self.kernel,self.theta) mu_pred = k_pred.T.dot(self.Sigma_inv).dot(self.fMAP) #covariance Sigma_testloc = self.create_Gramian(X_pred,X_pred,self.kernel,self.theta) '''Alternative formula that exploits posterior covariance (this seems to work) ''' A = self.Sigma_inv - self.Sigma_inv.dot(self.posterior_covariance).dot(self.Sigma_inv) Sigma_pred = Sigma_testloc - k_pred.T.dot(A).dot(k_pred) #is_PSD = is_positive_definite(Sigma_pred) return mu_pred,Sigma_pred def mu_pred(self,X_pred): ''' Predict posterior mean for SINGLE vector: needed for optimizers ''' k_pred = self.create_Gramian_nonsquare(self.X,X_pred.reshape(1,self.D),self.kernel,self.theta) mu_pred = k_pred.T.dot(self.Sigma_inv).dot(self.fMAP) return float(mu_pred) def mu_pred_neq(self,X_pred): return -self.mu_pred(X_pred)
class GUI_session: """ Class for handling Graphical User Inteterface: """ def __init__(self,PPBO_settings): """ Basic settings """ self.D = PPBO_settings.D #Problem dimension self.bounds = PPBO_settings.original_bounds #Boundaries of each variables as a sequence of tuplets self.alpha_grid_distribution = PPBO_settings.alpha_grid_distribution self.user_feedback_grid_size = 100 #How many possible points in the user feedback grid? self.FP = FeedbackProcessing(self.D, self.user_feedback_grid_size,self.bounds,self.alpha_grid_distribution,PPBO_settings.TGN_speed) self.current_xi = None self.current_x = None self.current_xi_grid = None self.user_feedback = None #variable to store user feedback self.user_feedback_was_given = False self.popup_configuration_movie_has_been_called = False self.results = pd.DataFrame(columns=(['alpha_xi_x' + str(i) for i in range(1,6+1)] + ['xi' + str(i) for i in range(1,6+1)] + ['alpha_star']),dtype=np.float64) #The output of the session is a dataframe containing user feedback ''' Auxiliary functions ''' def set_xi(self,xi): self.current_xi = xi def set_x(self,x): self.current_x = x def create_xi_grid(self): self.current_xi_grid = self.FP.xi_grid(xi=self.current_xi,x=self.current_x, alpha_grid_distribution='evenly', alpha_star=None,m=self.user_feedback_grid_size, is_scaled=False) def create_movie_of_configuration(self): """ Create 'movie.traj'. """ trajectory = [] for i in range(self.current_xi_grid.shape[0]): conf = np.array([list(self.current_xi_grid[i,:])]) function_arguments = pd.DataFrame(data=conf, columns=['camp_dx','camp_dy','camp_origin_height','alpha','beta','gamma']) function_arguments = function_arguments.to_dict('records')[0] trajectory.append(create_geometry(**function_arguments)) ase.io.write(path_from_root_to_files+'movie.traj',images=trajectory) def popup_configuration_movie(self): if not self.popup_configuration_movie_has_been_called: movie = ase.io.read(path_from_root_to_files+'movie.traj', index=':') ase.visualize.view(movie) else: PROCNAME = "python" #AD-HOC SOLUTION TO SHUT DOWN PREVIOUS GUI WINDOW!!!!! for proc in psutil.process_iter(): if proc.name() == PROCNAME: proc.kill() movie = ase.io.read(path_from_root_to_files+'movie.traj', index=':') ase.visualize.view(movie) self.popup_configuration_movie_has_been_called = True def popup_message(self): popup = tk.Tk() popup.attributes('-topmost', True) #The message is always on the top #popup.wm_title("Which configuration you expect to achieve the lowest energy?") popup.wm_title(" ") label = ttk.Label(popup, text="Type number: ", font=("Helvetica", 14)) E1 = ttk.Entry(popup, text="") B2 = ttk.Button(popup, text="Confirm", command = lambda: self.buttom_action("confirm",popup,E1)) B3 = ttk.Button(popup, text="I dont't know.", command = lambda: self.buttom_action("dont_know",popup,E1)) label.pack(side="top", fill="x", pady=10) E1.pack() B2.pack() B3.pack() popup.mainloop() def buttom_action(self,user_input,popup,E1): """ Decides what happens when one of three buttons is clicked """ if user_input=="confirm": typed_value = float(E1.get()) if typed_value>self.user_feedback_grid_size or typed_value < 1: print("Invalid input!") typed_value = 1 self.user_feedback = self.current_xi_grid[(int(typed_value)-1),:] #i.e. typed value - 1 !! print("--- Feedback ---") print("Typed value: " + str(typed_value)) print("... converted to: " + str(self.user_feedback)) self.user_feedback_was_given = True elif user_input=="dont_know": raise NotImplementedError else: print("Error, something strange happened!") popup.destroy() #Shut down tkinter.Tk() instance time.sleep(0.2) def save_results(self): res = pd.DataFrame(columns=(['alpha_xi_x' + str(i) for i in range(1,6+1)] + ['xi' + str(i) for i in range(1,6+1)] + ['alpha_star']),dtype=np.float64) xi = self.current_xi x = self.current_x alpha_xi_x = self.user_feedback alpha_star = np.nanmin(alpha_xi_x[x==0]/xi[x==0]) #every component in alpha_xi_x[x==0]/xi[x==0] should be same new_row = list(alpha_xi_x) + list(xi) + [alpha_star] res.loc[0,:] = new_row self.results=self.results.append(res, ignore_index=True) def run_iteration(self,allow_feedback): """ Runs one iteration of the session. Makes sure that configurations are set correctly. """ self.create_xi_grid() if allow_feedback: while not self.user_feedback_was_given: self.create_movie_of_configuration() self.popup_configuration_movie() self.popup_message() print("Iteration done!") self.user_feedback_was_given = False self.save_results()
class GPModel: """ Class for GP utility function model. Some methods are not inherited but composited from Feedback_Processing class """ COVARIANCE_SHRINKAGE = 1e-4 #An amount of shrinkage applied to Sigma #Something between 1e-5 - 1e-1 depending how far off are hyperparameter values (low value induces more accurate estimates but is numerically more unstable) def __init__(self, PPBO_settings): """ Initializes the GP_Model object """ self.verbose = PPBO_settings.verbose self.FP = None self.D = PPBO_settings.D #Problem dimension self.original_bounds = PPBO_settings.original_bounds #Boundaries of each variables as a sequence of tuplets self.bounds = ((0, 1), ) * self.D self.X = None #Design Matrix self.N = None #Number of observations including pseudo-observations self.m = PPBO_settings.n_pseudoobservations #How many pseudo-observations per one observation? self.obs_indices = None #Locations of true observations self.pseudobs_indices = None #Locations of pseudo-observations self.latest_obs_indices = None #Location of latest true observation given all observations from 0 to N self.alpha_grid_distribution = PPBO_settings.alpha_grid_distribution #evenly, cauchy or normal self.TGN_speed = PPBO_settings.TGN_speed self.n_gausshermite_sample_points = PPBO_settings.n_gausshermite_sample_points self.xi_acquisition_function = PPBO_settings.xi_acquisition_function self.theta_initial = PPBO_settings.theta_initial self.theta = None #Most optimized theta self.Sigma = None self.Sigma_inv = None self.Lambda_MAP = None self.posterior_covariance = None self.f_MAP = None self.max_iter_fMAP_estimation = PPBO_settings.max_iter_fMAP_estimation self.fMAP_optimizer = PPBO_settings.fMAP_optimizer self.mu_star_finding_trials = PPBO_settings.mu_star_finding_trials self.mu_star_previous_iteration = 0 self.mu_star_ = None self.x_star_ = None ''' --- Wrapper functions --- ''' def update_feedback_processing_object(self, X_obs): if self.FP is None: self.FP = FeedbackProcessing(self.D, self.m, self.original_bounds, self.alpha_grid_distribution, self.TGN_speed) self.FP.initialize_data(X_obs) else: self.FP.update_data(X_obs) def update_data(self): self.X = self.FP.X self.N = self.FP.N self.obs_indices = self.FP.obs_indices self.pseudobs_indices = self.FP.pseudobs_indices self.latest_obs_indices = self.FP.latest_obs_indices def update_model(self, optimize_theta=False): if self.theta is None: self.set_theta() self.update_Sigma(self.theta) self.update_Sigma_inv(self.theta) self.update_f_MAP(random_initialvalue=True) if optimize_theta: self.optimize_theta() self.update_f_MAP(random_initialvalue=True) self.update_Sigma(self.theta) self.update_Sigma_inv(self.theta) if self.verbose: print("Current theta is: " + str(self.theta) + ' (Acq. = ' + str(self.xi_acquisition_function) + ')') if self.verbose: print("Updating Lambda_MAP...") start = time.time() self.Lambda_MAP = self.create_Lambda(self.f_MAP, self.theta[0]) if self.verbose: print("... this took " + str(time.time() - start) + " seconds.") if self.verbose: print("Updating posterior covariance...") start = time.time() try: self.posterior_covariance_inv = self.Sigma_inv - self.Lambda_MAP self.posterior_covariance = pd_inverse( self.posterior_covariance_inv) except: print('---!!!--- Posterior covariance matrix is not PSD ---!!!---') pass if self.verbose: print("... this took " + str(time.time() - start) + " seconds.") if self.verbose: print("Computing mu_star and x_star ...") start = time.time() self.x_star_, self.mu_star_ = self.mu_star() if self.verbose: print("... this took " + str(time.time() - start) + " seconds.") ''' Auxiliary function ''' def is_pseudobs(self, i): return self.FP.is_pseudobs(i) ''' --- Kernels, hyper-parameters and covariance matrix --- ''' @staticmethod def SE_kernel(X1, X2, theta): l = theta[1] sigma_f = theta[2] if l <= 0 or sigma_f <= 0: print("Check hyperparameter values!") """ Compute the Euclidean distance between each row of X1 and X2 """ X1sq = np.sum(np.square(X1), 1) X2sq = np.sum(np.square(X2), 1) sqdist = -2. * np.dot(X1, X2.T) + (X1sq[:, None] + X2sq[None, :]) sqdist = np.clip(sqdist, 0, np.inf) return sigma_f**2 * np.exp(-0.5 * sqdist / (l**2)) @staticmethod def create_Gramian(X1, X2, kernel, *args): Sigma = kernel(X1, X2, *args) ''' REGULARIZATION OF THE COVARIANCE MATRIX''' Sigma = regularize_covariance(Sigma, GPModel.COVARIANCE_SHRINKAGE) return Sigma @staticmethod def create_Gramian_nonsquare(X1, X2, kernel, *args): Sigma = kernel(X1, X2, *args) return Sigma def update_Sigma(self, theta): self.Sigma = self.create_Gramian(self.X, self.X, self.SE_kernel, theta) #print("The condition number of the covariance matrix: " + str(np.linalg.cond(self.Sigma,p=np.inf))) #condition number of A = ||A||*||A^-1||, where the norm ||.|| is the maximum absolute row sum (since p =np.inf) def update_Sigma_inv(self, theta): self.Sigma_inv = pd_inverse(self.Sigma) def set_theta(self): self.theta = self.theta_initial if self.theta[1] is None: self.theta[1] = 10 if self.theta[2] is None: self.theta[2] = 0.001 if self.theta[0] is None: self.theta[ 0] = 0.001 #if too low, then zeros encountered in sigmas.... but too much noise leads to very inaccurate estimates mu_star, etc...!!! ''' --- Functional T --- ''' ''' Auxiliary functions for computing transormations/vectroizations of Phi ''' def sum_Phi(self, i, order_of_derivative, f, sigma, sample_points=None, weights=None): ''' Auxiliary function that computes summation of Phi-function values i = index of x observation, i.e. some element of list 'obs_indices' order_of_derivative = how many this the c.d.f of the standard normal (Phi) is differentiated? f = vector of f-values ''' m = self.m #Delta_i_j = (f[i+j+1]-f[i])/(sigma_) Delta = f[i + 1:i + m + 1] Delta = (Delta - f[i]) / sigma sum_ = 0 if order_of_derivative == 0: for j in range(0, m): #sum_ = sum_ + integrate(lambda x: std_normal_cdf(Delta[j]+x)*std_normal_pdf(x), -np.inf, np.inf)[0] #Do integration by using Gaussian-Hermite quadrature: sum_ = sum_ + (1 / np.sqrt(np.pi)) * np.dot( weights, std_normal_cdf(Delta[j] - np.sqrt(2) * sample_points) ) #Do to change of variables to get integteragl into the form int exp(x^2)*....dx. This is why np.sqrt(2) return (sum_) if order_of_derivative == 1: for j in range(0, m): sum_ = sum_ + float(var2_normal_pdf(Delta[j])) return (sum_) if order_of_derivative == 2: for j in range(0, m): sum_ = sum_ - 0.5 * Delta[j] * float(var2_normal_pdf(Delta[j])) return (sum_) else: print("The derivatives of an order higher than 2 are not needed!") return None def sum_Phi_vec(self, order_of_derivative, f, sigma, over_all_indices=False): ''' Auxiliary function that create a vector of sum_Phi with elements as i = obs_indices[0],...,obs_indices[len(obs_indices)], if not over_all_indices i = 1,...,N if over_all_indices ''' sample_points, weights = np.polynomial.hermite.hermgauss( self.n_gausshermite_sample_points) #for cross-correlation integral if not over_all_indices: sum_Phi_vec_ = [ self.sum_Phi(i, order_of_derivative, f, sigma, sample_points, weights) for i in self.obs_indices ] return np.array(sum_Phi_vec_) else: sum_Phi_vec_ = list( chain.from_iterable([ [ self.sum_Phi(i, order_of_derivative, f, sigma, sample_points, weights) ] * (self.m + 1) for i in self.obs_indices ])) #for z indices just replicate previous x value return np.array(sum_Phi_vec_) ''' Derivatives of the functional T of the order: 0,1,2 ''' def T(self, f, theta, Sigma_inv_=None): if Sigma_inv_ is None: Sigma_inv_ = self.Sigma_inv sumPhi = self.sum_Phi_vec(0, f, theta[0]) T = -0.5 * f.T.dot(Sigma_inv_).dot(f) - np.sum(sumPhi) / self.m return T def T_grad(self, f, theta, Sigma_inv_=None): if Sigma_inv_ is None: Sigma_inv_ = self.Sigma_inv N = self.N m = self.m latest_obs_indices = self.latest_obs_indices beta = np.zeros((N, 1)).reshape(N, ) Phi_der_vec_ = np.array([ float(var2_normal_pdf( (f[j] - f[latest_obs_indices[j]]) / theta[0])) for j in self.pseudobs_indices ]) sum_Phi_der_vec_ = self.sum_Phi_vec(1, f, theta[0]) beta[self.obs_indices] = sum_Phi_der_vec_ / (theta[0] * m) beta[self.pseudobs_indices] = -Phi_der_vec_ / (theta[0] * m) T_grad = -Sigma_inv_.dot(f).reshape(N, ) + beta.reshape(N, ) return T_grad def T_hessian(self, f, theta, Sigma_inv_=None): if Sigma_inv_ is None: Sigma_inv_ = self.Sigma_inv Lambda = self.create_Lambda(f, theta[0]) T_hessian = -Sigma_inv_ + Lambda return T_hessian def create_Lambda(self, f, sigma): N = self.N m = self.m sample_points, weights = np.polynomial.hermite.hermgauss( self.n_gausshermite_sample_points) ''' Diagonal matrix ''' Lambda_diagonal = [0] * N constant = 1 / (m * (sigma**2)) for i in range(N): if not self.is_pseudobs(i): Lambda_diagonal[i] = -constant * self.sum_Phi( i, 2, f, sigma) #Note that sum_Phi used so minus sign needed! else: latest_x_index = np.max( [ind for ind in self.obs_indices if ind < i]) Delta_i = (f[i] - f[latest_x_index]) / sigma Lambda_diagonal[ i] = 0.5 * constant * Delta_i * var2_normal_pdf(Delta_i) Lambda_diagonal = np.diag(Lambda_diagonal) ''' Upper triangular off-diagonal matrix''' Lambda_uppertri = np.zeros((N, N)) for row_ind in range(N): if not self.is_pseudobs(row_ind): for col_ind in range(row_ind + 1, row_ind + m + 1): if self.is_pseudobs(col_ind): Delta = (f[col_ind] - f[row_ind]) / sigma Lambda_uppertri[ row_ind, col_ind] = -0.5 * constant * Delta * var2_normal_pdf( Delta) ''' Final Lambda is sum of diagonal + upper_tringular + lower_tringular ''' Lambda = Lambda_diagonal + Lambda_uppertri + Lambda_uppertri.T return Lambda ''' --- Evidence --- ''' def evidence(self, theta, f_initial): print('---------- Iter results ----------------') Sigma_ = self.create_Gramian(self.X, self.X, self.SE_kernel, theta) Sigma_inv_ = pd_inverse(Sigma_) f_MAP_ = scipy.optimize.minimize( lambda f: -self.T(f, theta, Sigma_inv_), f_initial, method='trust-exact', jac=lambda f: -self.T_grad(f, theta, Sigma_inv_), hess=lambda f: -self.T_hessian(f, theta, Sigma_inv_), options={ 'disp': False, 'maxiter': 200 }).x Lambda_MAP = self.create_Lambda(f_MAP_, theta[0]) ''' A straightforward numerically unstable implementation ''' #I = np.eye(self.N) #matrix = I+Sigma_.dot(Lambda_MAP) #(sign, logdet) = np.linalg.slogdet(matrix) #determinant = sign*np.exp(logdet) #evidence = np.exp(self.T(f_MAP_,theta,Sigma_inv_))*np.power(determinant,-0.5) #log_evidence = self.T(f_MAP_,theta,Sigma_inv_) - 0.5*np.log(determinant) ''' A numerically stable implementation based on Cholesky-decomposition ''' try: posterior_covariance_inv = Sigma_inv_ - Lambda_MAP posterior_covariance = pd_inverse(posterior_covariance_inv) L = np.linalg.cholesky(posterior_covariance) log_determinant = 2 * np.sum(np.log(np.diag(L))) log_evidence = self.T( f_MAP_, theta, Sigma_inv_ ) - 0.5 * log_determinant #determinant still too large, so resulting theta gives too regularized GP print('(scaled) Log-evidence: ' + str(log_evidence)) print("Log-determinant: " + str(log_determinant)) print('Hyper-parameters: ' + str(theta)) return log_evidence except: print('Theta = ' + str(theta) + ' gave a non-PSD covariance matrix.') return -1e+3 ''' --- Optimizations --- ''' def update_f_MAP(self, random_initialvalue=False): ''' Finds MAP estimate and updates it ''' if self.f_MAP is None: f_initial = np.random.multivariate_normal([0] * self.N, self.Sigma).reshape( self.N, 1) elif len( self.f_MAP ) < self.N and not random_initialvalue: #Last iteration map estimate is of course a vector of shorter length. Hence add enough constant (=mean of f_MAP) to the tail of the map estimate. f_MAP = np.insert(self.f_MAP, len(self.f_MAP), [np.mean(self.f_MAP)] * (self.N - len(self.f_MAP))) f_initial = f_MAP elif len(self.f_MAP) == self.N and not random_initialvalue: f_initial = self.f_MAP else: f_initial = np.random.multivariate_normal([0] * self.N, self.Sigma).reshape( self.N, 1) if self.verbose: print("MAP-estimation begins...") start = time.time() ''' Optimizer: choose second-order either trust-krylov or trust-exact, but trust-exact does not work with pypet multiprocessing! ''' if self.fMAP_optimizer == 'trust-krylov': verbose = False else: verbose = self.verbose res = scipy.optimize.minimize( lambda f: -self.T(f, self.theta), f_initial, method=self.fMAP_optimizer, jac=lambda f: -self.T_grad(f, self.theta), hess=lambda f: -self.T_hessian(f, self.theta), options={ 'disp': verbose, 'maxiter': self.max_iter_fMAP_estimation }) if self.verbose: print('... this took ' + str(time.time() - start) + ' seconds.') self.f_MAP = res.x def optimize_theta(self): ''' Optimizes all hyper-parameters (including noise) by maximizing the evidence ''' print("Hyper-parameter optimization begins...") start = time.time() #BayesianOptimization #Higher lengthscale generates more accurate GP mean (and location of maximizer of mean) #However, higher lengthscale also generates less accurate argmax distribution (argmax samples are widepread) ''' Bounds for hyperparameters given that COVARIANCE_SHRINKAGE = 1e-4 ''' bounds = [ { 'name': 'sigma', 'type': 'continuous', 'domain': (1e-3, 2e-3) }, #Too low noise gives non-PSD covariance matrix { 'name': 'leghtscale', 'type': 'continuous', 'domain': (5, 80) }, #D high ---> l low { 'name': 'sigma_l', 'type': 'continuous', 'domain': (1e-3, 2e-3) } ] # since f is a utility function this parameter make no much sense. BO = BayesianOptimization( lambda theta: -self.evidence(theta[ 0], self.f_MAP), #theta[0] since need to unnest list one level domain=bounds, optimize_restarts=0, normalize_Y=True) BO.run_optimization(max_iter=50) if self.verbose: print('Optimization of hyper-parameters took ' + str(time.time() - start) + ' seconds.') self.theta = BO.x_opt if self.verbose: print("The resulting theta is " + str(self.theta)) def mu_star(self, mu_star_finding_trials=None): ''' Function finds the optimal predictive mean and the maximizer x''' if mu_star_finding_trials is None: mu_star_finding_trials = self.mu_star_finding_trials #Global seach with constraints (DIFFERENTIAL EVOLUTION OPTIMIZATION) bounds = self.bounds min_ = 10**24 for i in range(0, mu_star_finding_trials ): #How many times mu_star is tried to find? res = scipy.optimize.differential_evolution(self.mu_pred_neq, bounds, updating='immediate', disp=False) #print(res.x) #to monitor how stable are results if res.fun < min_: min_ = res.fun x_star = res.x mu_star = self.mu_pred(x_star) return x_star, mu_star ''' --- GP predictions --- ''' def mu_Sigma_pred(self, X_pred): ''' Predictive posterior means and covariances ''' #mean k_pred = self.create_Gramian_nonsquare(self.X, X_pred, self.SE_kernel, self.theta) mu_pred = k_pred.T.dot(self.Sigma_inv).dot(self.f_MAP) #covariance Sigma_testloc = self.create_Gramian(X_pred, X_pred, self.SE_kernel, self.theta) '''Alternative formula that exploits posterior covariance (this seems to work) ''' A = self.Sigma_inv - self.Sigma_inv.dot(self.posterior_covariance).dot( self.Sigma_inv) Sigma_pred = Sigma_testloc - k_pred.T.dot(A).dot(k_pred) #is_PSD = is_positive_definite(Sigma_pred) return mu_pred, Sigma_pred def mu_pred(self, X_pred): ''' Predict posterior mean for SINGLE vector: needed for optimizers ''' k_pred = self.create_Gramian_nonsquare(self.X, X_pred.reshape(1, self.D), self.SE_kernel, self.theta) mu_pred = k_pred.T.dot(self.Sigma_inv).dot(self.f_MAP) return float(mu_pred) def mu_pred_neq(self, X_pred): return -self.mu_pred(X_pred)
class GUI_session: """ Class for handling Graphical User Inteterface: """ def __init__(self, PPBO_settings): """ Basic settings """ self.D = PPBO_settings.D #Problem dimension self.bounds = PPBO_settings.original_bounds #Boundaries of each variables as a sequence of tuplets self.alpha_grid_distribution = PPBO_settings.alpha_grid_distribution self.preference_feedback_size = 100 #How many possible points in the user feedback grid? self.confidence_feedback_size = 5 #How many possible points in the user feedback grid? self.FP = FeedbackProcessing(self.D, self.preference_feedback_size, self.bounds, self.alpha_grid_distribution, PPBO_settings.TGN_speed) self.current_xi = None self.current_x = None self.current_xi_grid = None self.user_feedback_preference = None #variable to store user feedback for preference self.user_feedback_confidence = None #variable to store user feedback for confidence degree self.user_feedback_was_given = False self.popup_configuration_movie_has_been_called = False self.movie = None self.results_preference = pd.DataFrame( columns=(['alpha_xi_x' + str(i) for i in range(1, 6 + 1)] + ['xi' + str(i) for i in range(1, 6 + 1)] + ['alpha_star']), dtype=np.float64 ) #The output of the session is a dataframe containing user feedback self.results_confidence = pd.DataFrame( columns=(['x' + str(i) for i in range(1, 6 + 1)] + ['xi' + str(i) for i in range(1, 6 + 1)] + ['confidence']), dtype=np.float64 ) #The output of the session is a dataframe containing user feedback ''' Auxiliary functions ''' def set_xi(self, xi): self.current_xi = xi def set_x(self, x): self.current_x = x def create_xi_grid(self): self.current_xi_grid = self.FP.xi_grid( xi=self.current_xi, x=self.current_x, alpha_grid_distribution='equispaced', alpha_star=None, m=self.preference_feedback_size, is_scaled=False) def create_movie_of_configuration(self): """ Create 'movie.traj'. """ trajectory = [] for i in range(self.current_xi_grid.shape[0]): conf = np.array([list(self.current_xi_grid[i, :])]) function_arguments = pd.DataFrame(data=conf, columns=[ 'camp_dx', 'camp_dy', 'camp_origin_height', 'alpha', 'beta', 'gamma' ]) function_arguments = function_arguments.to_dict('records')[0] trajectory.append(create_geometry(**function_arguments)) ase.io.write(path_from_root_to_files + 'movie.traj', images=trajectory) movie = ase.io.read(path_from_root_to_files + 'movie.traj', index=':') self.movie = movie def getMiniGUI(self): view = nglview.show_asetraj(self.movie) view.parameters = dict(background_color='white', camera_type='perpective', camera_fov=15) view._camera_orientation = [ -28.583735327243016, -0.2970873285220947, 1.198387795047608, 0, -0.3455812695981218, 28.584920668432527, -1.1563751171127739, 0, -1.1853133653976955, -1.1697730312356562, -28.561879887836003, 0, -7.061999797821045, -8.524999618530273, -8.855999946594238, 1 ] #Default camera view button = widgets.Button(description='Confirm', disabled=False, button_style='') slider = widgets.IntSlider(min=0, max=self.confidence_feedback_size - 1, step=1, description='Confidence: ', value=0, continuous_update=False, readout=True, layout=widgets.Layout(width='60%', height='80px', position='right')) def confirm(event): pref_feedback = int(view.frame) conf_feedback = int(slider.value) self.user_feedback_preference = self.current_xi_grid[( pref_feedback), :] self.user_feedback_confidence = conf_feedback self.user_feedback_was_given = True button.on_click(confirm) return view, button, slider def save_results(self): res_preference = pd.DataFrame( columns=(['alpha_xi_x' + str(i) for i in range(1, 6 + 1)] + ['xi' + str(i) for i in range(1, 6 + 1)] + ['alpha_star']), dtype=np.float64) res_confidence = pd.DataFrame( columns=(['x' + str(i) for i in range(1, 6 + 1)] + ['xi' + str(i) for i in range(1, 6 + 1)] + ['confidence']), dtype=np.float64) xi = self.current_xi x = self.current_x alpha_xi_x_preference = self.user_feedback_preference confidence = self.user_feedback_confidence alpha_star_preference = np.nanmin( alpha_xi_x_preference[x == 0] / xi[x == 0] ) #every component in alpha_xi_x[x==0]/xi[x==0] should be same new_row_preference = list(alpha_xi_x_preference) + list(xi) + [ alpha_star_preference ] new_row_confidence = list(x) + list(xi) + [confidence] res_preference.loc[0, :] = new_row_preference res_confidence.loc[0, :] = new_row_confidence self.results_preference = self.results_preference.append( res_preference, ignore_index=True) self.results_confidence = self.results_confidence.append( res_confidence, ignore_index=True) def initialize_iteration(self, x, xi): self.set_x(x) self.set_xi(xi) self.create_xi_grid() self.create_movie_of_configuration()
class GUI_session: """ Class for handling Graphical User Inteterface: """ def __init__(self, PPBO_settings): """ Basic settings """ self.D = PPBO_settings.D #Problem dimension self.bounds = PPBO_settings.original_bounds #Boundaries of each variables as a sequence of tuplets self.alpha_grid_distribution = PPBO_settings.alpha_grid_distribution self.user_feedback_grid_size = PPBO_settings.user_feedback_grid_size #How many possible points in the user feedback grid? self.n_irfs_periods = 20 #This must be same as in a model.mod file self.FP = FeedbackProcessing(self.D, self.user_feedback_grid_size, self.bounds, self.alpha_grid_distribution, PPBO_settings.TGN_speed) self.current_xi = None self.current_x = None self.current_xi_grid = None self.dsge_results = None self.user_feedback = None #variable to store user feedback self.user_feedback_was_given = False self.popup_slider_has_been_called = False self.results = pd.DataFrame( columns=(['alpha_xi_x' + str(i) for i in range(1, self.D + 1)] + ['xi' + str(i) for i in range(1, self.D + 1)] + ['alpha_star']), dtype=np.float64 ) #The output of the session is a dataframe containing user feedback self.engine = 'OCTAVE' ''' Prepare model files to temp ''' self.path_to_dsge = os.getcwd() + '/dsge' #CHECK THAT THIS IS CORRECT if os.path.exists(self.path_to_dsge + '/temp'): shutil.rmtree(self.path_to_dsge + '/temp') os.mkdir(self.path_to_dsge + '/temp') for i in range(self.user_feedback_grid_size): shutil.copy2('dsge/US_FU19_rep.mod', 'dsge/temp/US_FU19_rep_' + str(i) + '.mod') shutil.copy2('dsge/prior_function_US_FU19.m', 'dsge/temp/prior_function_US_FU19.m') ''' Auxiliary functions ''' def set_xi(self, xi): self.current_xi = xi def set_x(self, x): self.current_x = x def create_xi_grid(self): self.current_xi_grid = self.FP.xi_grid( xi=self.current_xi, x=self.current_x, alpha_grid_distribution='equispaced', alpha_star=None, m=self.user_feedback_grid_size, is_scaled=False) def simulate_DSGE(self): """ Generate IRFs given prior parameters """ # dsge_results = [] # for i in range(self.current_xi_grid.shape[0]): # conf = list(self.current_xi_grid[i,:]) # print("lambda = "+str(conf)) # if self.engine == 'OCTAVE': # self.octave.push('param0',conf[0]) # self.octave.push('param1',conf[1]) # self.octave.push('param2',conf[2]) # self.octave.push('param3',conf[3]) # self.octave.push('param4',conf[4]) # self.octave.push('param5',conf[5]) # self.octave.push('param6',conf[6]) # self.octave.run('simulate_DSGE.m',verbose=False) # dsge_results.append({'irf_dy_eZ': self.octave.pull('irf_dy_eZ'),'irf_dc_eZ': self.octave.pull('irf_dc_eZ')}) # else: # self.eng.eval('param0'+str(conf[0])+";", nargout=0) # self.eng.eval('param1'+str(conf[1])+";", nargout=0) # self.eng.eval('param2'+str(conf[2])+";", nargout=0) # self.eng.eval('param3'+str(conf[3])+";", nargout=0) # self.eng.eval('param4'+str(conf[4])+";", nargout=0) # self.eng.eval('param5'+str(conf[5])+";", nargout=0) # self.eng.eval('param6'+str(conf[6])+";", nargout=0) # self.eng.simulate_DSGE(nargout=0) #simulate_DSGE is the filename of matlab-script # dsge_results.append({'irf_dy_eZ': self.eng.workspace['irf_dy_eZ'],'irf_dc_eZ': self.eng.workspace['irf_dc_eZ']}) #Multi-procesessing loop for non-picklable objects @delayed @wrap_non_picklable_objects def func_async_wrapped(conf, i, *args): octave = Oct2Py() octave.addpath( r'/usr/lib/dynare/matlab') #CHECK THAT THIS IS CORRECT octave.cd(self.path_to_dsge + '/temp') octave.push('param0', conf[0]) octave.push('param1', conf[1]) octave.push('param2', conf[2]) octave.push('param3', conf[3]) octave.push('param4', conf[4]) octave.push('param5', conf[5]) octave.push('param6', conf[6]) octave.eval('dynare US_FU19_rep_' + str(i) + ' noclearall nolog', verbose=False) results = { 'irf_dy_eZ': octave.pull('irf_dy_eZ'), 'irf_dc_eZ': octave.pull('irf_dc_eZ'), 'irf_labobs_eZ': octave.pull('irf_labobs_eZ'), 'irf_dw_eZ': octave.pull('irf_dw_eZ') } octave.exit() del octave return results self.dsge_results = Parallel( n_jobs=-2, backend='loky', temp_folder='tmp')( func_async_wrapped(list(self.current_xi_grid[i, :]), i) for i in range(self.current_xi_grid.shape[0])) def save_results(self): res = pd.DataFrame( columns=(['alpha_xi_x' + str(i) for i in range(1, self.D + 1)] + ['xi' + str(i) for i in range(1, self.D + 1)] + ['alpha_star']), dtype=np.float64) xi = self.current_xi x = self.current_x alpha_xi_x = self.user_feedback alpha_star = np.nanmin( alpha_xi_x[x == 0] / xi[x == 0] ) #every component in alpha_xi_x[x==0]/xi[x==0] should be same new_row = list(alpha_xi_x) + list(xi) + [alpha_star] res.loc[0, :] = new_row self.results = self.results.append(res, ignore_index=True) def irf(self, ind, var): irfs = self.dsge_results[ind] if var == 'dy': irf_ = irfs['irf_dy_eZ'] elif var == 'dc': irf_ = irfs['irf_dc_eZ'] elif var == 'labobs': irf_ = irfs['irf_labobs_eZ'] elif var == 'dw': irf_ = irfs['irf_dw_eZ'] irf_ = np.array(irf_) #irf_.shape (414, 20) return irf_ def prepare_app(self): """ Prepares one iteration of the user session. Makes sure that configurations are set correctly. """ self.create_xi_grid() self.simulate_DSGE() ''' --- GUI plot/slider --- ''' #https://pypi.org/project/jupyter-ui-poll/ # plt.rcParams['figure.dpi'] = 127 # fig = plt.figure() # fig.set_figheight(3.6) # ax1 = fig.add_subplot(221) # ax2 = fig.add_subplot(222, sharex=ax1, sharey=ax1) # ax3 = fig.add_subplot(223, sharex=ax1, sharey=ax1) # ax4 = fig.add_subplot(224, sharex=ax1, sharey=ax1) # fig.suptitle('IRFs to an shock to TFP', fontsize=12, fontweight='bold') # plt.subplots_adjust(left=0.07, bottom=0.19, hspace=0.42) # self.x_axis_points = list(range(1,self.n_irfs_periods+1)) # ax3.set_xlabel('Period') # ax4.set_xlabel('Period') # ax1.title.set_text('dy') # ax2.title.set_text('dc') # ax3.title.set_text('labobs') # ax4.title.set_text('dw') # ax1.axhline(y=0, color='k', linestyle='dashed') # ax2.axhline(y=0, color='k', linestyle='dashed') # ax3.axhline(y=0, color='k', linestyle='dashed') # ax4.axhline(y=0, color='k', linestyle='dashed') # ax1.set_ylim([-0.1, 0.1]) # ax2.set_ylim([-0.1, 0.1]) # ax3.set_ylim([-0.1, 0.1]) # ax4.set_ylim([-0.1, 0.1]) # #fig.text(0.1, 0.01, "$\it{Please\ select\ the\ most\ realistic\ hyperparameter\ configuration. }$") plt.rcParams['figure.dpi'] = 135 fig = plt.figure() fig.set_figheight(3.9) ax1 = fig.add_subplot(221) ax2 = fig.add_subplot(222, sharex=ax1) ax3 = fig.add_subplot(223, sharex=ax1) ax4 = fig.add_subplot(224, sharex=ax1) fig.suptitle('IRFs to an shock to TFP', fontsize=12, fontweight='bold') plt.subplots_adjust(left=0.07, bottom=0.19, hspace=0.42) self.x_axis_points = list(range(1, self.n_irfs_periods + 1)) ax3.set_xlabel('Period') ax4.set_xlabel('Period') ax1.title.set_text('dy') ax2.title.set_text('dc') ax3.title.set_text('labobs') ax4.title.set_text('dw') ax1.axhline(y=0, color='k', linestyle='dashed') ax2.axhline(y=0, color='k', linestyle='dashed') ax3.axhline(y=0, color='k', linestyle='dashed') ax4.axhline(y=0, color='k', linestyle='dashed') #fig.text(0.1, 0.01, "$\it{Please\ select\ the\ most\ realistic\ hyperparameter\ configuration. }$") #l1 = ax1.errorbar(self.x_axis_points, np.mean(self.irf(0,'dy'),axis=0), np.std(self.irf(0,'dy'),axis=0), elinewidth=0.7, label='IRF_dy') #l2 = ax2.errorbar(self.x_axis_points, np.mean(self.irf(0,'dc'),axis=0), np.std(self.irf(0,'dc'),axis=0), elinewidth=0.7, label='IRF_dc') #l3 = ax3.errorbar(self.x_axis_points, np.mean(self.irf(0,'labobs'),axis=0), np.std(self.irf(0,'labobs'),axis=0), elinewidth=0.7, label='IRF_labobs') #l4 = ax4.errorbar(self.x_axis_points, np.mean(self.irf(0,'dw'),axis=0), np.std(self.irf(0,'dw'),axis=0), elinewidth=0.7, label='IRF_dw') ax1.set_ylim([-0.2 + 0.1, 0.6 + 0.1]) ax2.set_ylim([-0.1 + 0.1, 0.3 + 0.1]) ax3.set_ylim([-0.3 + 0.1, 0.1 + 0.1]) ax4.set_ylim([-0.05 + 0.1, 0.15 + 0.1]) ax1.set_yticks([-0.2, 0, 0.2, 0.4, 0.6]) ax2.set_yticks([-0.1, 0, 0.1, 0.2, 0.3]) ax3.set_yticks([-0.3, 0.2, -0.1, 0, 0.1]) ax4.set_yticks([-0.05, 0, 0.05, 0.1, 0.15]) l1, = ax1.plot(self.x_axis_points, np.mean(self.irf(0, 'dy'), axis=0), lw=2) l2, = ax2.plot(self.x_axis_points, np.mean(self.irf(0, 'dc'), axis=0), lw=2) l3, = ax3.plot(self.x_axis_points, np.mean(self.irf(0, 'labobs'), axis=0), lw=2) l4, = ax4.plot(self.x_axis_points, np.mean(self.irf(0, 'dw'), axis=0), lw=2) plt.draw() slider = widgets.IntSlider(min=1, max=self.user_feedback_grid_size, step=1, description='Slider: ', value=1, continuous_update=False, readout=False, layout=widgets.Layout(width='60%', height='80px', position='right')) button = widgets.Button(description='Confirm', icon='fa-check', button_style='success', layout=Layout(width='90px')) def confirm(event): typed_value = int(slider.value) self.user_feedback = self.current_xi_grid[(int(typed_value) - 1), :] self.user_feedback_was_given = True plt.close('all') button.on_click(confirm) plt.show(block=False) return button, slider, fig, l1, l2, l3, l4 def update_plot(self, l1, l2, l3, l4, fig, slider): param_ind = int(slider.value - 1) #update_errorbar(l1, self.x_axis_points, np.mean(self.irf(param_ind,'dy'),axis=0), xerr=None, yerr=np.std(self.irf(0,'dy'),axis=0)) #update_errorbar(l2, self.x_axis_points, np.mean(self.irf(param_ind,'dc'),axis=0), xerr=None, yerr=np.std(self.irf(0,'dc'),axis=0)) #update_errorbar(l3, self.x_axis_points, np.mean(self.irf(param_ind,'labobs'),axis=0), xerr=None, yerr=np.std(self.irf(0,'labobs'),axis=0)) #update_errorbar(l4, self.x_axis_points, np.mean(self.irf(param_ind,'dw'),axis=0), xerr=None, yerr=np.std(self.irf(0,'dw'),axis=0)) l1.set_ydata(np.mean(self.irf(param_ind, 'dy'), axis=0)) l2.set_ydata(np.mean(self.irf(param_ind, 'dc'), axis=0)) l3.set_ydata(np.mean(self.irf(param_ind, 'labobs'), axis=0)) l4.set_ydata(np.mean(self.irf(param_ind, 'dw'), axis=0)) fig.canvas.draw_idle()