示例#1
0
文件: gp_model.py 项目: AaltoPML/PPBO
 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)
示例#2
0
文件: gui.py 项目: P-Mikkola/PPBO
    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')
示例#3
0
文件: GUI.py 项目: AaltoPML/PPBO
 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
示例#4
0
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)
示例#5
0
文件: GUI.py 项目: AaltoPML/PPBO
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()
示例#6
0
文件: gp_model.py 项目: AaltoPML/PPBO
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)
示例#7
0
文件: gui.py 项目: P-Mikkola/PPBO
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()
示例#8
0
文件: gui.py 项目: P-Mikkola/PPBO
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()