docs: def optimize(self, init_method='default', inference=None, n_times=10, perturb=False, pertSize=1e-3, verbose=None):
          """
          Train the model using the specified initialization strategy
  
          Args:
              init_method:    initialization strategy:
                                  'default': variance is equally split across the different random effect terms. For mulit-trait models the empirical covariance between traits is used
                                  'random': variance component parameters (scales) are sampled from a normal distribution with mean 0 and std 1,
                                  None: no initialization is considered. Initial parameters can be specifies by using the single covariance getTraitCovarfun() 
              inference:      inference gp method, by default algebrically efficient inference (i.e., gp2kronSum, gp2KronSumLR, gp3KronSumLR) will be used when possible.
                              For models with high a standard inference scheme (gp_base) will be used. 
              n_times:        number of restarts to converge
              perturb:        if true, the initial point (if random initializaiton is not being used) is perturbed with gaussian noise for each restart (default, False)
              perturbSize:    std of the gassian noise used to perturb the initial point
              verbose:        print if convergence is achieved and how many restarted were needed
          """
          #verbose = dlimix.getVerbose(verbose)
          assert init_method in ['default', 'random', None], 'VarianceDecomposition: specified init_method not valid'
          if init_method=='default':  self._init_params_default()
  
          # determine if repeats should be considered 
          if init_method is not 'random' and not perturb:     n_times = 1
  
          # determine inference
          if inference is None:   inference = self._det_inference()
          else:                   self._check_inference(inference)
          self._inference = inference
  
          if self.gp is None:        self._initGP()
  
          params0 = self.gp.getParams()
          for i in range(n_times):
              if init_method=='random':   
                  params = {'covar': sp.randn(params0['covar'].shape[0])}
                  self.gp.setParams(params)
              elif perturb:
                  params = {'covar': params0['covar'] + pertSize * sp.randn(params0['covar'].shape[0])}
                  self.gp.setParams(params)
              conv, info = self.gp.optimize()
              if conv:    break
  
          if verbose:
              if conv==False:
                  print('No local minimum found for the tested initialization points')
              else:
                  print(('Local minimum found at iteration %d' % i))
  
          return conv