Performs Black Box Variational Inference

        Parameters
        ----------
        posterior : method
            Hands bbvi_fit a posterior object

        optimizer : string
            Stochastic optimizer: either RMSProp or ADAM.

        iterations: int
            How many iterations to run

        print_progress : bool
            Whether tp print the ELBO progress or not
        
        start_diffuse : bool
            Whether to start from diffuse values (if not: use approx Gaussian)

        Returns
        ----------
        BBVIResults object

def _bbvi_fit(self, optimizer='RMSProp', iterations=1000, print_progress=True,
        start_diffuse=False, **kwargs):
        """ Performs Black Box Variational Inference

        Parameters
        ----------
        posterior : method
            Hands bbvi_fit a posterior object

        optimizer : string
            Stochastic optimizer: either RMSProp or ADAM.

        iterations: int
            How many iterations to run

        print_progress : bool
            Whether tp print the ELBO progress or not
        
        start_diffuse : bool
            Whether to start from diffuse values (if not: use approx Gaussian)

        Returns
        ----------
        BBVIResults object
        """
        if self.model_name2 in ["t", "Skewt"]:
            default_learning_rate = 0.0001
        else:
            default_learning_rate = 0.001

        animate = kwargs.get('animate', False)
        batch_size = kwargs.get('batch_size', 24) 
        learning_rate = kwargs.get('learning_rate', default_learning_rate) 
        record_elbo = kwargs.get('record_elbo', False) 

        # Starting values
        gaussian_latents = self._preoptimize_model() # find parameters for Gaussian model
        phi = self.latent_variables.get_z_starting_values()
        q_list = self.initialize_approx_dist(phi, start_diffuse, gaussian_latents)

        # PERFORM BBVI
        bbvi_obj = ifr.CBBVI(self.neg_logposterior, self.log_p_blanket, q_list, batch_size, 
            optimizer, iterations, learning_rate, record_elbo)

        if print_progress is False:
            bbvi_obj.printer = False

        if animate is True:
            q, q_params, q_ses, stored_z, stored_predictive_likelihood = bbvi_obj.run_and_store()
            self._animate_bbvi(stored_z,stored_predictive_likelihood)
        else:
            q, q_params, q_ses, elbo_records = bbvi_obj.run()

        self.latent_variables.set_z_values(q_params[:self.z_no],'BBVI',np.exp(q_ses[:self.z_no]),None)    

        # STORE RESULTS
        for k in range(len(self.latent_variables.z_list)):
            self.latent_variables.z_list[k].q = q[k]

        theta = q_params[self.z_no:]
        Y = self.data
        scores = None
        states = q_params[self.z_no:]
        X_names = None
        states_ses = np.exp(q_ses[self.z_no:])

        self.states = states
        self.states_ses = states_ses

        return res.BBVISSResults(data_name=self.data_name,X_names=X_names,model_name=self.model_name,
            model_type=self.model_type, latent_variables=self.latent_variables,data=Y,index=self.index,
            multivariate_model=self.multivariate_model,objective=self.neg_logposterior(q_params), 
            method='BBVI',ses=q_ses[:self.z_no],signal=theta,scores=scores,elbo_records=elbo_records,
            z_hide=self._z_hide,max_lag=self.max_lag,states=states,states_var=np.power(states_ses,2))