jiahuimbzuai/CoQuIR huggingface dataset:
//  [python]
def _get_optimal_ws_and_biases(self, preacts, labels):
         
        def eval_func(x):
            ws = np.array(x)

            vs_logits = preacts*ws[None,:]
            log_sum_exp = scipy.special.logsumexp(a=vs_logits, axis=1) 
            exp_vs_logits = np.exp(vs_logits)
            sum_exp = np.sum(exp_vs_logits, axis=1)

            log_likelihoods = (np.sum(vs_logits*labels,axis=1)
                               - log_sum_exp)
            nll = -np.mean(log_likelihoods)

            grads_ws = preacts*(labels - (exp_vs_logits/sum_exp[:,None]))

            #multiply by -1 because we care about *negative* log likelihood
            mean_grads_ws = -np.mean(grads_ws, axis=0)

            return nll, mean_grads_ws

        if (self.verbose):
            original_nll = compute_nll(labels=labels, preacts=preacts,
                                       t=1.0, bs=np.zeros(labels.shape[1]))
            print("Original NLL is: ",original_nll)
            
        optimization_result = scipy.optimize.minimize(
                      fun=eval_func,
                      #fun=lambda x: eval_func(x)[0],
                      x0=np.array([1.0 for x in range(preacts.shape[1])]),
                      bounds=[(0,None) for x in range(preacts.shape[1])],
                      jac=True,
                      method='L-BFGS-B',
                      tol=1e-07,
                      **self.lbfgs_kwargs)
        if (self.verbose):
            print(optimization_result)
        assert optimization_result.success==True, optimization_result
        
        ws = optimization_result.x 
        return ws
//  [python]
def tabular_soft_q_iter(R, T, maxiter=5000, verbose=False, Q_init=None, learning_rate=1, ftol=0, gamma=0.99):
  '''Adapted from https://github.com/rddy/isql/blob/master/1.0-tabular-ime.ipynb'''

  T = deepcopy(T) # s',s,a
  T = np.exp(T)
  T = np.swapaxes(T, 0, 1) # s,s',a
  T = np.swapaxes(T, 1, 2) # s,a,s'

  n, m = R.shape[:2]
  Q = np.zeros((n, m)) if Q_init is None else deepcopy(Q_init)
  prevQ = deepcopy(Q)
  if verbose:
    diffs = []
  for iter_idx in range(maxiter):
    V = np.max(prevQ, axis=1)
    V_broad = V.reshape((1, 1, n))
    Q = np.sum(T * (R + gamma * V_broad), axis=2)
    Q = (1 - learning_rate) * prevQ + learning_rate * Q
    diff = np.mean((Q - prevQ)**2)/(np.std(Q)**2)
    if verbose:
      diffs.append(diff)
    if diff < ftol:
      break
    prevQ = deepcopy(Q)
  if verbose:
    plt.xlabel('Number of Iterations')
    plt.ylabel('Avg. Squared Bellman Error')
    plt.title('Soft Q Iteration')
    plt.plot(diffs)
    plt.yscale('log')
    plt.show()

  Q -= np.max(Q, axis=1, keepdims=True)

  return Q
//  [python]
def tabular_soft_q_iter(R, T, maxiter=5000, verbose=False, Q_init=None, learning_rate=1, ftol=0, gamma=0.99):
  '''Adapted from https://github.com/rddy/isql/blob/master/1.0-tabular-ime.ipynb'''

  T = deepcopy(T) # s',s,a
  T = np.exp(T)
  T = np.swapaxes(T, 0, 1) # s,s',a
  T = np.swapaxes(T, 1, 2) # s,a,s'

  n, m = R.shape[:2]
  Q = np.zeros((n, m)) if Q_init is None else deepcopy(Q_init)
  prevQ = deepcopy(Q)
  if verbose:
    diffs = []
  for iter_idx in range(maxiter):
    V = scipy.special.logsumexp(prevQ, axis=1)
    V_broad = V.reshape((1, 1, n))
    Q = np.sum(T * (R + gamma * V_broad), axis=2)
    Q = (1 - learning_rate) * prevQ + learning_rate * Q
    diff = np.mean((Q - prevQ)**2)/(np.std(Q)**2)
    if verbose:
      diffs.append(diff)
    if diff < ftol:
      break
    prevQ = deepcopy(Q)
  if verbose:
    plt.xlabel('Number of Iterations')
    plt.ylabel('Avg. Squared Bellman Error')
    plt.title('Soft Q Iteration')
    plt.plot(diffs)
    plt.yscale('log')
    plt.show()

  Q -= scipy.special.logsumexp(Q, axis=1, keepdims=True)

  return Q
//  [python]
def reduce_logsumexp(x, axis=None, keepdims=False):
  if tf.is_tensor(x):
    return tf.reduce_logsumexp(x, axis=axis, keepdims=keepdims)
  if torch.is_tensor(x):
    return x.logsumexp(dim=_torch_axis(x, axis), keepdim=keepdims)
  return s.logsumexp(x, axis=axis, keepdims=keepdims)
//  [python]
def reduce_logsumexp(x, axis=None, keepdims=False):
  if tf.is_tensor(x):
    return tf.reduce_logsumexp(x, axis=axis, keepdims=keepdims)
  if torch.is_tensor(x):
    return x.logsumexp(dim=_torch_axis(x, axis), keepdim=keepdims)
  return logsumexp(x, axis=axis, keepdims=keepdims)
//  [python]
def _get_optimal_ws_and_biases(self, preacts, labels):
         
        def eval_func(x):
            ws = np.array(x[:int(len(x)/2)])
            bs = np.array(x[int(len(x)/2):]) 

            vs_logits = preacts*ws[None,:] + bs[None,:]
            log_sum_exp = np.max(a=vs_logits, axis=1) + np.log(np.sum(np.exp(vs_logits - np.max(vs_logits, axis=1)[:,None]), axis=1))
            exp_vs_logits = np.exp(vs_logits)
            sum_exp = np.sum(exp_vs_logits, axis=1)

            log_likelihoods = (np.sum(vs_logits*labels,axis=1)
                               - log_sum_exp)
            nll = -np.mean(log_likelihoods)

            grads_ws = preacts*(labels - (exp_vs_logits/sum_exp[:,None]))
            grads_b = labels - (exp_vs_logits/sum_exp[:,None])

            #multiply by -1 because we care about *negative* log likelihood
            mean_grads_ws = -np.mean(grads_ws, axis=0)
            mean_grads_b = -np.mean(grads_b, axis=0) 

            return nll, np.array(list(mean_grads_ws)+list(mean_grads_b))

        if (self.verbose):
            original_nll = compute_nll(labels=labels, preacts=preacts,
                                       t=1.0, bs=np.zeros(labels.shape[1]))
            print("Original NLL is: ",original_nll)
            
        optimization_result = scipy.optimize.minimize(
                      fun=eval_func,
                      #fun=lambda x: eval_func(x)[0],
                      x0=np.array([1.0 for x in range(preacts.shape[1])]
                                  +[0.0 for x in range(preacts.shape[1])]),
                      bounds=[(0,None) for x in range(preacts.shape[1])]
                              +[(None,None) for x in range(preacts.shape[1])],
                      jac=True,
                      method='L-BFGS-B',
                      tol=1e-07,
                      **self.lbfgs_kwargs)
        if (self.verbose):
            print(optimization_result)
        assert optimization_result.success==True, optimization_result
        
        ws = optimization_result.x[:preacts.shape[1]] 
        bs = optimization_result.x[preacts.shape[1]:]
        return ws, bs
//  [python]
def _get_optimal_ws_and_biases(self, preacts, labels):
         
        def eval_func(x):
            ws = np.array(x[:int(len(x)/2)])
            bs = np.array(x[int(len(x)/2):]) 

            vs_logits = preacts*ws[None,:] + bs[None,:]
            log_sum_exp = scipy.special.logsumexp(a=vs_logits, axis=1) 
            exp_vs_logits = np.exp(vs_logits)
            sum_exp = np.sum(exp_vs_logits, axis=1)

            log_likelihoods = (np.sum(vs_logits*labels,axis=1)
                               - log_sum_exp)
            nll = -np.mean(log_likelihoods)

            grads_ws = preacts*(labels - (exp_vs_logits/sum_exp[:,None]))
            grads_b = labels - (exp_vs_logits/sum_exp[:,None])

            #multiply by -1 because we care about *negative* log likelihood
            mean_grads_ws = -np.mean(grads_ws, axis=0)
            mean_grads_b = -np.mean(grads_b, axis=0) 

            return nll, np.array(list(mean_grads_ws)+list(mean_grads_b))

        if (self.verbose):
            original_nll = compute_nll(labels=labels, preacts=preacts,
                                       t=1.0, bs=np.zeros(labels.shape[1]))
            print("Original NLL is: ",original_nll)
            
        optimization_result = scipy.optimize.minimize(
                      fun=eval_func,
                      #fun=lambda x: eval_func(x)[0],
                      x0=np.array([1.0 for x in range(preacts.shape[1])]
                                  +[0.0 for x in range(preacts.shape[1])]),
                      bounds=[(0,None) for x in range(preacts.shape[1])]
                              +[(None,None) for x in range(preacts.shape[1])],
                      jac=True,
                      method='L-BFGS-B',
                      tol=1e-07,
                      **self.lbfgs_kwargs)
        if (self.verbose):
            print(optimization_result)
        assert optimization_result.success==True, optimization_result
        
        ws = optimization_result.x[:preacts.shape[1]] 
        bs = optimization_result.x[preacts.shape[1]:]
        return ws, bs
//  [python]
def do_regularized_tempscale_optimization(labels, preacts, beta, verbose,
                                          lbfgs_kwargs):
    #beta is the regularization parameter
    def eval_func(x):
        t = x[0]
        bs = np.array(x[1:])
        #tsb = temp_scaled_biased
        tsb_preacts = preacts/float(t) + bs[None,:]
        log_sum_exp = np.max(tsb_preacts, axis=1) + np.log(np.sum(np.exp(tsb_preacts - np.max(tsb_preacts, axis=1)[:,None]), axis=1))

        exp_tsb_logits = np.exp(tsb_preacts)
        sum_exp = np.sum(exp_tsb_logits, axis=1)
        sum_preact_times_exp = np.sum(preacts*exp_tsb_logits, axis=1)

        notsb_logits_trueclass =\
            np.sum(preacts*labels, axis=1)
        tsb_logits_trueclass =\
            np.sum(tsb_preacts*labels, axis=1)
        
        log_likelihoods = tsb_logits_trueclass - log_sum_exp
        objective = -np.mean(log_likelihoods) + beta*np.sum(np.square(bs))
        grads_t = ((sum_preact_times_exp/sum_exp
                    - notsb_logits_trueclass)/\
                    (float(t)**2))
        grads_b = labels - (exp_tsb_logits/(sum_exp[:,None]))
        #multiply by -1 because we care about *negative* log likelihood
        mean_grad_t = -np.mean(grads_t)
        mean_grads_b = ((-np.mean(grads_b, axis=0)) + (2*bs*beta))
        return objective, np.array([mean_grad_t]+list(mean_grads_b))

    if (verbose):
        original_nll = compute_nll(labels=labels, preacts=preacts,
                                   t=1.0, bs=np.zeros(labels.shape[1]))
        print("Original NLL is: ",original_nll)
        
    optimization_result = scipy.optimize.minimize(
                              fun=eval_func,
                              #fun=lambda x: eval_func(x)[0],
                              x0=np.array([1.0]+[0.0 for x in
                                                 range(labels.shape[1])]),
                              bounds=[(0,None)]+[(None,None) for x in
                                                 range(labels.shape[1])],
                              jac=True,
                              method='L-BFGS-B',
                              tol=1e-07,
                              **lbfgs_kwargs)
    if (verbose):
        print("Optimization Result:")
        print(optimization_result)
    assert optimization_result.success==True, optimization_result
    optimal_t = optimization_result.x[0]
    biases = np.array(optimization_result.x[1:])
    final_nll = compute_nll(labels=labels, preacts=preacts,
                            t=optimal_t, bs=biases)
    if (verbose):
        print("Final NLL & grad is: ",final_nll)

    return (optimal_t, biases)
//  [python]
def do_regularized_tempscale_optimization(labels, preacts, beta, verbose,
                                          lbfgs_kwargs):
    #beta is the regularization parameter
    def eval_func(x):
        t = x[0]
        bs = np.array(x[1:])
        #tsb = temp_scaled_biased
        tsb_preacts = preacts/float(t) + bs[None,:]
        log_sum_exp = scipy.special.logsumexp(a=tsb_preacts, axis=1) 

        exp_tsb_logits = np.exp(tsb_preacts)
        sum_exp = np.sum(exp_tsb_logits, axis=1)
        sum_preact_times_exp = np.sum(preacts*exp_tsb_logits, axis=1)

        notsb_logits_trueclass =\
            np.sum(preacts*labels, axis=1)
        tsb_logits_trueclass =\
            np.sum(tsb_preacts*labels, axis=1)
        
        log_likelihoods = tsb_logits_trueclass - log_sum_exp
        objective = -np.mean(log_likelihoods) + beta*np.sum(np.square(bs))
        grads_t = ((sum_preact_times_exp/sum_exp
                    - notsb_logits_trueclass)/\
                    (float(t)**2))
        grads_b = labels - (exp_tsb_logits/(sum_exp[:,None]))
        #multiply by -1 because we care about *negative* log likelihood
        mean_grad_t = -np.mean(grads_t)
        mean_grads_b = ((-np.mean(grads_b, axis=0)) + (2*bs*beta))
        return objective, np.array([mean_grad_t]+list(mean_grads_b))

    if (verbose):
        original_nll = compute_nll(labels=labels, preacts=preacts,
                                   t=1.0, bs=np.zeros(labels.shape[1]))
        print("Original NLL is: ",original_nll)
        
    optimization_result = scipy.optimize.minimize(
                              fun=eval_func,
                              #fun=lambda x: eval_func(x)[0],
                              x0=np.array([1.0]+[0.0 for x in
                                                 range(labels.shape[1])]),
                              bounds=[(0,None)]+[(None,None) for x in
                                                 range(labels.shape[1])],
                              jac=True,
                              method='L-BFGS-B',
                              tol=1e-07,
                              **lbfgs_kwargs)
    if (verbose):
        print("Optimization Result:")
        print(optimization_result)
    assert optimization_result.success==True, optimization_result
    optimal_t = optimization_result.x[0]
    biases = np.array(optimization_result.x[1:])
    final_nll = compute_nll(labels=labels, preacts=preacts,
                            t=optimal_t, bs=biases)
    if (verbose):
        print("Final NLL & grad is: ",final_nll)

    return (optimal_t, biases)
//  [python]
def to_llr(x, name=None):
  ''' Convert a matrix of probabilities into log-likelihood ratio
  :math:`LLR = log(\\frac{prob(data|target)}{prob(data|non-target)})`
  '''
  with tf.name_scope(name, "log_likelihood_ratio", [x]):
    nb_classes = x.shape.as_list()[-1]
    new_arr = []
    for j in range(nb_classes):
      scores_copy = tf.transpose(
          tf.gather(tf.transpose(x), [i for i in range(nb_classes) if i != j]))
      scores_copy -= tf.expand_dims(x[:, j], axis=-1)
      new_arr.append(-s.logsumexp(scores_copy, 1))
    return tf.concat(new_arr, axis=-1) + np.log(13)
//  [python]
def to_llr(x, name=None):
  ''' Convert a matrix of probabilities into log-likelihood ratio
  :math:`LLR = log(\\frac{prob(data|target)}{prob(data|non-target)})`
  '''
  with tf.name_scope(name, "log_likelihood_ratio", [x]):
    nb_classes = x.shape.as_list()[-1]
    new_arr = []
    for j in range(nb_classes):
      scores_copy = tf.transpose(
          tf.gather(tf.transpose(x), [i for i in range(nb_classes) if i != j]))
      scores_copy -= tf.expand_dims(x[:, j], axis=-1)
      new_arr.append(-logsumexp(scores_copy, 1))
    return tf.concat(new_arr, axis=-1) + np.log(13)
//  [python]
def cond_log_prob(self, inputs, targets, absolute_normalization=False):
    if isinstance(inputs, str):
      self._cond_log_prob_cache.append(inputs)
      probs = [np.log(1 / len(targets))] * len(targets)
    else:
      self._cond_log_prob_cache.extend(inputs)
      probs = [[np.log(1 / len(t))] * len(t) for t in targets]
    if not absolute_normalization:
      probs -= s.logsumexp(probs)
      probs = [p.tolist() for p in probs]
    return list(probs)
//  [python]
def cond_log_prob(self, inputs, targets, absolute_normalization=False):
    if isinstance(inputs, str):
      self._cond_log_prob_cache.append(inputs)
      probs = [np.log(1 / len(targets))] * len(targets)
    else:
      self._cond_log_prob_cache.extend(inputs)
      probs = [[np.log(1 / len(t))] * len(t) for t in targets]
    if not absolute_normalization:
      probs -= scipy.special.logsumexp(probs)
      probs = [p.tolist() for p in probs]
    return list(probs)
//  [python]
def optimize_eta(eta, word_count, beta_doc_kv):
    def f(eta,  word_count, beta_doc_kv):
        # precomputation
        eta = np.insert(eta, K - 1, 0)
        Ndoc = int(np.sum(word_count))
        part1 = np.dot(
            word_count, (eta.max() + np.log(np.exp(eta - eta.max()) @ beta_doc_kv))
        ) - Ndoc * np.log(np.sum(np.exp(eta)))
        part2 = 0.5 * (eta[:-1] - mu).T @ siginv @ (eta[:-1] - mu)
        print(part2 - part1)
        return np.float32(part2 - part1)
    def df(eta,  word_count, beta_doc_kv):
        """gradient for the objective of the variational update q(etas)"""
        # precomputation
        eta = np.insert(eta, K - 1, 0)
        # formula
        part1 = np.delete(
            beta_doc_kv @ (word_count / np.sum(beta_doc_kv.T, axis=1))
            - np.sum(word_count) / np.sum(np.exp(eta)),
            K - 1,
        )
        part2 = siginv @ (eta[:-1] - mu)
        # We want to maximize f, but numpy only implements minimize, so we
        # minimize -f
        print(part2 - part1)
        return (part2-part1)

    return optimize.minimize(
        f, x0=eta, args=(word_count, beta_doc_kv), jac=df, options={'maxiter': 500, 'gtol': 1e-5, 'eps':10},
    )
//  [python]
def optimize_eta(eta, word_count, beta_doc_kv):
    def f(eta,  word_count, beta_doc_kv):
        # precomputation
        eta = np.insert(eta, K - 1, 0)
        Ndoc = int(np.sum(word_count))
        part1 = np.dot(
            word_count, (eta.max() + np.log(np.exp(eta - eta.max()) @ beta_doc_kv))
        ) - Ndoc * scipy.special.logsumexp(eta)
        part2 = 0.5 * (eta[:-1] - mu).T @ siginv @ (eta[:-1] - mu)
        print(part2 - part1)
        return np.float32(part2 - part1)
    def df(eta,  word_count, beta_doc_kv):
        """gradient for the objective of the variational update q(etas)"""
        # precomputation
        eta = np.insert(eta, K - 1, 0)
        # formula
        part1 = np.delete(
            beta_doc_kv @ (word_count / np.sum(beta_doc_kv.T, axis=1))
            - np.sum(word_count) / np.sum(np.exp(eta)),
            K - 1,
        )
        part2 = siginv @ (eta[:-1] - mu)
        # We want to maximize f, but numpy only implements minimize, so we
        # minimize -f
        print(part2 - part1)
        return (part2-part1)

    return optimize.minimize(
        f, x0=eta, args=(word_count, beta_doc_kv), jac=df, options={'maxiter': 500, 'gtol': 1e-5, 'eps':10},
    )
//  [python]
def logsumexp(x, y=None, axis=None):
    if y is None:
        return s.logsumexp(x, axis=axis)
    else:
        return s.logsumexp(np.array([x,y]), axis=axis)
//  [python]
def logsumexp(x, y=None, axis=None):
    if y is None:
        return scipy.special.logsumexp(x, axis=axis)
    else:
        return scipy.special.logsumexp(np.array([x,y]), axis=axis)
//  [python]
def value(self, theta_flat, lmbda, idx):
        x = self.X[idx]
        y = self.y[idx]
        theta = theta_flat.reshape(self.n_features, self.n_classes)

        prod = safe_sparse_dot(x, theta)
        loss = (-prod[y == 1] + s.logsumexp(prod, axis=1)).mean()
        regul = 0
        if self.reg == 'exp':
            alpha = np.exp(lmbda)
            regul = 0.5 * alpha @ (theta * theta).sum(axis=0)
        return loss + 0.5 * regul
//  [python]
def value(self, theta_flat, lmbda, idx):
        x = self.X[idx]
        y = self.y[idx]
        theta = theta_flat.reshape(self.n_features, self.n_classes)

        prod = safe_sparse_dot(x, theta)
        loss = (-prod[y == 1] + sc.logsumexp(prod, axis=1)).mean()
        regul = 0
        if self.reg == 'exp':
            alpha = np.exp(lmbda)
            regul = 0.5 * alpha @ (theta * theta).sum(axis=0)
        return loss + 0.5 * regul
//  [python]
def __call__(self, latent_pi: jnp.ndarray) -> Tuple[jnp.ndarray, None]:
        """ :return: probability of each actions. We keep the convention of torch.distributions."""
        action_logits = hk.Linear(self.action_dim, **init_weights())(latent_pi)
        action_logits = action_logits - scipy.special.logsumexp(action_logits, axis=-1, keepdims=True) 
        mean_actions = jnp.exp(action_logits)
        mean_acitons = nn.softmax(mean_actions, axis=1)
        return mean_actions, action_logits
//  [python]
def cond_log_prob(self, inputs, targets, absolute_normalization=False):
        assert (not isinstance(targets, str)), "targets in cond_log_prob must be a list (or a list of lists if inputs is a list). targets was instead a str."

        def generate_dummy_scores(inpt, tgts):
            self.n_mult_choice += 1
            unnorm_log_probs = [np.log(1 / (i + 1)) for i, _ in enumerate(tgts)]
            if len(unnorm_log_probs) > 1:
                # we want to make sure tasks can correctly handle responses which are
                # assigned zero probability
                unnorm_log_probs[0] = -np.inf

            if absolute_normalization:
                log_probs = unnorm_log_probs
            else:
                log_normalizer = np.logaddexp.reduce(unnorm_log_probs)
                log_probs = [lp - log_normalizer for lp in unnorm_log_probs]

            return log_probs

        if isinstance(inputs, str):
            scores = generate_dummy_scores(inputs, targets)
        elif isinstance(inputs, list):
            scores = [
                generate_dummy_scores(inpt, tgts) for inpt, tgts in zip(inputs, targets)
            ]
        else:
            raise ValueError("inputs has unexpected type %s" % type(inputs))

        self.add_to_transcript(inputs, scores, targets)
        return scores
//  [python]
def cond_log_prob(self, inputs, targets, absolute_normalization=False):
        assert (not isinstance(targets, str)), "targets in cond_log_prob must be a list (or a list of lists if inputs is a list). targets was instead a str."

        def generate_dummy_scores(inpt, tgts):
            self.n_mult_choice += 1
            unnorm_log_probs = [np.log(1 / (i + 1)) for i, _ in enumerate(tgts)]
            if len(unnorm_log_probs) > 1:
                # we want to make sure tasks can correctly handle responses which are
                # assigned zero probability
                unnorm_log_probs[0] = -np.inf

            if absolute_normalization:
                log_probs = unnorm_log_probs
            else:
                log_normalizer = scipy.special.logsumexp(unnorm_log_probs)
                log_probs = [lp - log_normalizer for lp in unnorm_log_probs]

            return log_probs

        if isinstance(inputs, str):
            scores = generate_dummy_scores(inputs, targets)
        elif isinstance(inputs, list):
            scores = [
                generate_dummy_scores(inpt, tgts) for inpt, tgts in zip(inputs, targets)
            ]
        else:
            raise ValueError("inputs has unexpected type %s" % type(inputs))

        self.add_to_transcript(inputs, scores, targets)
        return scores
//  [python]
def logsumexp(x, axis=-1):
    return s.logsumexp(x, axis=axis)
//  [python]
def logsumexp(x, axis=-1):
    return scipy.special.logsumexp(x, axis=axis)
//  [python]
def mu_lower_log_cdf(
      self, x: Union[float, Iterable[float]]
  ) -> Union[float, np.ndarray]:
    """Computes log cumulative density function of the mu_lower distribution.

    For ADD adjacency type:
      mu_lower(x) := sum of sampling_probs[i] * mu(x - sensitivities[i])
    For REMOVE adjacency type:
      mu_lower(x) := mu

    Args:
      x: the point or points at which the log of the cumulative density function
        is to be calculated.

    Returns:
      The log of the cumulative density function of the mu_lower distribution at
      x, i.e., the log of the probability that mu_lower is less than or equal to
      x.
    """
    if self.adjacency_type == AdjacencyType.ADD:
      points_per_sens = np.add.outer(np.atleast_1d(x), -self.sensitivities)
      logcdf_per_sens = self.noise_log_cdf(points_per_sens)
      output = np.log(np.sum(np.exp(logcdf_per_sens), axis=1))
      if isinstance(x, numbers.Number):
        return output[0]
      else:
        return output
    else:  # Case: self.adjacency_type == AdjacencyType.REMOVE
      return self.noise_log_cdf(x)
//  [python]
def mu_lower_log_cdf(
      self, x: Union[float, Iterable[float]]
  ) -> Union[float, np.ndarray]:
    """Computes log cumulative density function of the mu_lower distribution.

    For ADD adjacency type:
      mu_lower(x) := sum of sampling_probs[i] * mu(x - sensitivities[i])
    For REMOVE adjacency type:
      mu_lower(x) := mu

    Args:
      x: the point or points at which the log of the cumulative density function
        is to be calculated.

    Returns:
      The log of the cumulative density function of the mu_lower distribution at
      x, i.e., the log of the probability that mu_lower is less than or equal to
      x.
    """
    if self.adjacency_type == AdjacencyType.ADD:
      points_per_sens = np.add.outer(np.atleast_1d(x), -self.sensitivities)
      logcdf_per_sens = self.noise_log_cdf(points_per_sens)
      output = scipy.special.logsumexp(
          logcdf_per_sens, axis=1, b=self.sampling_probs
      )
      if isinstance(x, numbers.Number):
        return output[0]
      else:
        return output
    else:  # Case: self.adjacency_type == AdjacencyType.REMOVE
      return self.noise_log_cdf(x)
//  [python]
def calc_seq_loglike(seq, logits, method='smart', plot=False):
    """
    seq: Amino acid sequence (str)
    logits: A [seq_length x vocab_size] array
    """
    
    # Convert to integer seq, and drop first token since 
    # logits are next char predictions.
    iseq = aa_seq_to_int(seq)[1:]
    iseq = [i-1 for i in iseq] # subtract 1 as logits dont consider pad.
    
    if plot:
        plt.figure(figsize=(20,4))
        plt.imshow(logits.T, aspect='auto')
        for i in range(len(iseq)):
            plt.plot(i, iseq[i], '.k')
        plt.show()
        
    if method == 'dumb':
        sm = scipy.special.softmax(logits, axis=1)
        
        log_like = 0
        for i in range(sm.shape[0]):
            log_like += np.log(sm[i, iseq[i]])
    
    if method == 'smart':
        
        lse = np.max(logits, axis=1) + np.log(np.sum(np.exp(logits - np.max(logits, axis=1, keepdims=True)), axis=1))
        aa_logits = np.array([logits[i, iseq[i]] for i in range(logits.shape[0])])
        
        log_like = np.sum(aa_logits - lse)                   
            
    return log_like
//  [python]
def calc_seq_loglike(seq, logits, method='smart', plot=False):
    """
    seq: Amino acid sequence (str)
    logits: A [seq_length x vocab_size] array
    """
    
    # Convert to integer seq, and drop first token since 
    # logits are next char predictions.
    iseq = aa_seq_to_int(seq)[1:]
    iseq = [i-1 for i in iseq] # subtract 1 as logits dont consider pad.
    
    if plot:
        plt.figure(figsize=(20,4))
        plt.imshow(logits.T, aspect='auto')
        for i in range(len(iseq)):
            plt.plot(i, iseq[i], '.k')
        plt.show()
        
    if method == 'dumb':
        sm = scipy.special.softmax(logits, axis=1)
        
        log_like = 0
        for i in range(sm.shape[0]):
            log_like += np.log(sm[i, iseq[i]])
    
    if method == 'smart':
        
        lse = scipy.special.logsumexp(logits, axis=1)
        aa_logits = np.array([logits[i, iseq[i]] for i in range(logits.shape[0])])
        
        log_like = np.sum(aa_logits - lse)                   
            
    return log_like
//  [python]
def get_posterior_lprobs(learned_param):
    with tf.Session() as sess:
        approx_model.load_param(learned_param, sess)
        
        posterior_lprobs_np = []

        for _ in range(n_iter):
            posterior_lprobs_i = sess.run(posterior_lprobs)

            posterior_lprobs_i = s.logsumexp(posterior_lprobs_i, axis=2, keepdims=True) - np.log(posterior_lprobs_i.shape[2])
            # (nclass, ndata, 1)

            posterior_lprobs_np.append(posterior_lprobs_i)
        
        posterior_lprobs_np = np.concatenate(posterior_lprobs_np, axis=2) # concat over the sample dimension
        # (nclass, ndata, n_param_samples)
        
        posterior_lprobs_np = s.logsumexp(posterior_lprobs_np, axis=2) - np.log(posterior_lprobs_np.shape[2])
    return posterior_lprobs_np
//  [python]
def get_posterior_lprobs(learned_param):
    with tf.Session() as sess:
        approx_model.load_param(learned_param, sess)
        
        posterior_lprobs_np = []

        for _ in range(n_iter):
            posterior_lprobs_i = sess.run(posterior_lprobs)

            posterior_lprobs_i = scipy.special.logsumexp(posterior_lprobs_i, axis=2, keepdims=True) - np.log(posterior_lprobs_i.shape[2])
            # (nclass, ndata, 1)

            posterior_lprobs_np.append(posterior_lprobs_i)
        
        posterior_lprobs_np = np.concatenate(posterior_lprobs_np, axis=2) # concat over the sample dimension
        # (nclass, ndata, n_param_samples)
        
        posterior_lprobs_np = scipy.special.logsumexp(posterior_lprobs_np, axis=2) - np.log(posterior_lprobs_np.shape[2])
    return posterior_lprobs_np
//  [python]
def log_marginal_likelihood(self, x, all_g, z_g=None, numpy = False):
        """Computes log p(x) in closed form using conjugacy properties of Dirichlet-Categorical
            x : [n_samples, n_vars]
            all_g : list of all possible igraph.Graph objects in domain; is exhaustively summed over
        """

        # log p(x, G)
        log_prob_obs_g = np.zeros(len(all_g))

        # normalizing constant for log p(G) using exhaustive normalization
        if z_g is None:
            z_g = self.g_dist.log_normalization_constant(all_g=all_g)

        # log p(x, G)
        for i, g in enumerate(tqdm.tqdm(all_g, desc='p(X) log_marginal_likelihood', disable=not self.verbose)):
            if numpy:
                g = mat_to_graph(g)
            # log p(x, G) = log (p(G)/Z) + log p(x | G)
            log_prob_obs_g[i] = self.g_dist.unnormalized_log_prob(g=g) - z_g \
                + self.log_marginal_likelihood_given_g(g=g, x=x)

        # log p(x) = log(sum_G exp(log p(x, G)))
        return s.logsumexp(log_prob_obs_g)
//  [python]
def log_marginal_likelihood(self, x, all_g, z_g=None, numpy = False):
        """Computes log p(x) in closed form using conjugacy properties of Dirichlet-Categorical
            x : [n_samples, n_vars]
            all_g : list of all possible igraph.Graph objects in domain; is exhaustively summed over
        """

        # log p(x, G)
        log_prob_obs_g = np.zeros(len(all_g))

        # normalizing constant for log p(G) using exhaustive normalization
        if z_g is None:
            z_g = self.g_dist.log_normalization_constant(all_g=all_g)

        # log p(x, G)
        for i, g in enumerate(tqdm.tqdm(all_g, desc='p(X) log_marginal_likelihood', disable=not self.verbose)):
            if numpy:
                g = mat_to_graph(g)
            # log p(x, G) = log (p(G)/Z) + log p(x | G)
            log_prob_obs_g[i] = self.g_dist.unnormalized_log_prob(g=g) - z_g \
                + self.log_marginal_likelihood_given_g(g=g, x=x)

        # log p(x) = log(sum_G exp(log p(x, G)))
        return scipy.special.logsumexp(log_prob_obs_g)
//  [python]
def log_prob_parameters_mc(self, theta, n_samples=3e4):
        """Approximates p(theta) using Monte Carlo integration
            theta : parameters
        """

        logliks = []
        for tt in range(int(n_samples)):

            # sample from p(G)
            g = self.g_dist.sample_G()

            # evaluate log prob p(theta | G)
            logliks.append(self.log_prob_parameters(theta=theta, g=g))

            # print
            if not tt % int(n_samples / 1000) and tt > 0:
                curr = s.logsumexp(
                    np.array(logliks[:tt + 1]) - np.log(tt + 1))
                print(f'iter = {tt}: log p(theta | G) [MC] = {curr}', end='\r')

        log_prob_obs = s.logsumexp(
            np.array(logliks) - np.log(n_samples))
        return log_prob_obs
//  [python]
def log_prob_parameters_mc(self, theta, n_samples=3e4):
        """Approximates p(theta) using Monte Carlo integration
            theta : parameters
        """

        logliks = []
        for tt in range(int(n_samples)):

            # sample from p(G)
            g = self.g_dist.sample_G()

            # evaluate log prob p(theta | G)
            logliks.append(self.log_prob_parameters(theta=theta, g=g))

            # print
            if not tt % int(n_samples / 1000) and tt > 0:
                curr = scipy.special.logsumexp(
                    np.array(logliks[:tt + 1]) - np.log(tt + 1))
                print(f'iter = {tt}: log p(theta | G) [MC] = {curr}', end='\r')

        log_prob_obs = scipy.special.logsumexp(
            np.array(logliks) - np.log(n_samples))
        return log_prob_obs
//  [python]
def log_marginal_likelihood_mc(self, x, n_samples=3e4):
        """Approximates normalization constant p(x) using Monte Carlo integration
            x : [n_samples, n_vars]
        """

        logliks = []
        for tt in range(int(n_samples)):

            # sample from p(G, theta) = p(G) p(theta | G)
            g = self.g_dist.sample_G()
            theta = self.sample_parameters(g=g)

            # evaluate likelihood log p(X | theta, G)
            logliks.append(self.log_likelihood(x=x, theta=theta, g=g))

            # print
            if not tt % int(n_samples / 1000) and tt > 0:
                curr = s.logsumexp(
                    (logliks[:tt + 1] - np.log(tt + 1)))
                print(f'iter = {tt}: log p(X) [MC] = {curr}', end='\r')
        print()
        log_prob_obs = s.logsumexp((logliks - np.log(n_samples)))
        return log_prob_obs
//  [python]
def log_marginal_likelihood_mc(self, x, n_samples=3e4):
        """Approximates normalization constant p(x) using Monte Carlo integration
            x : [n_samples, n_vars]
        """

        logliks = []
        for tt in range(int(n_samples)):

            # sample from p(G, theta) = p(G) p(theta | G)
            g = self.g_dist.sample_G()
            theta = self.sample_parameters(g=g)

            # evaluate likelihood log p(X | theta, G)
            logliks.append(self.log_likelihood(x=x, theta=theta, g=g))

            # print
            if not tt % int(n_samples / 1000) and tt > 0:
                curr = scipy.special.logsumexp(
                    (logliks[:tt + 1] - np.log(tt + 1)))
                print(f'iter = {tt}: log p(X) [MC] = {curr}', end='\r')
        print()
        log_prob_obs = scipy.special.logsumexp((logliks - np.log(n_samples)))
        return log_prob_obs
//  [python]
def log_marginal_likelihood_given_g_mc(self, x, g, n_samples=3e4):
        """Approximates p(x | G) using Monte Carlo integration
            x : [n_samples, n_vars]
            g : graph
        """

        logliks = []
        for tt in range(int(n_samples)):

            # sample from p(theta | G)
            theta = self.sample_parameters(g=g)

            # evaluate likelihood log p(X | theta, G)
            logliks.append(self.log_likelihood(x=x, theta=theta, g=g))

            # print
            if not tt % int(n_samples / 1000) and tt > 0:
                curr = np.log(np.sum(np.exp(np.array(logliks[:tt + 1]) - np.log(tt + 1))))
                print(f'iter = {tt}: log p(X | G) [MC] = {curr}', end='\r')

        log_prob_obs = np.log(np.sum(np.exp(np.array(logliks) - np.log(n_samples))))
        return log_prob_obs
//  [python]
def log_marginal_likelihood_given_g_mc(self, x, g, n_samples=3e4):
        """Approximates p(x | G) using Monte Carlo integration
            x : [n_samples, n_vars]
            g : graph
        """

        logliks = []
        for tt in range(int(n_samples)):

            # sample from p(theta | G)
            theta = self.sample_parameters(g=g)

            # evaluate likelihood log p(X | theta, G)
            logliks.append(self.log_likelihood(x=x, theta=theta, g=g))

            # print
            if not tt % int(n_samples / 1000) and tt > 0:
                curr = scipy.special.logsumexp(
                    np.array(logliks[:tt + 1]) - np.log(tt + 1))
                print(f'iter = {tt}: log p(X | G) [MC] = {curr}', end='\r')

        log_prob_obs = scipy.special.logsumexp(
            np.array(logliks) - np.log(n_samples))
        return log_prob_obs
//  [python]
def log_likelihood(self, parameters):

        interp = parameters['interp_mass_radius']
        masses = np.linspace(1., 2., num=1000)
        masses = masses[masses < interp.x[-1]]

        logl = np.logaddexp.reduce(self.KDE.logpdf((interp(masses), masses)))
        logl -= np.log(len(masses))

        return logl
//  [python]
def log_likelihood(self, parameters):

        interp = parameters['interp_mass_radius']
        masses = np.linspace(1., 2., num=1000)
        masses = masses[masses < interp.x[-1]]

        logl = scipy.special.logsumexp(self.KDE.logpdf((interp(masses), masses)))
        logl -= np.log(len(masses))

        return logl
//  [python]
def postprocess_multiallelic_calls(genotypes: np.ndarray, multiallelic_map: MultiAllelicMap, probs: np.ndarray):
    """
    Postprocesses genotype calls (biallelic) by making them compatible with
    multi-allelic variants.
    Idea is that variants that are part of the same multi-allelic variant
    need to be compatible (i.e. not conflicting genotypes)
    Uses the genotype probabilities (log-scale) to find the most likely genotypes
    within multiallelic variants

    Genotypes are numeric:
     0 or 1: 0/0, 2: 1/1, 3: 0/1

     Probs is matrix of shape n_variants x 3 where each
     column corresponds to log e prob of genotypes
     (0/0, 0/1, 1/1)


    """
    assert len(genotypes) == len(multiallelic_map.ravel()), len(multiallelic_map.ravel())
    original_genotypes = genotypes.copy()

    # nan-probs will cause problem
    n_nan = np.sum(np.isnan(probs))
    logging.info(f"{n_nan} probs are nan. Setting them to 1/3 before postprocesing genotypes")
    probs[np.isnan(probs)] = np.log(1/3)

    # normalize probs so that they sum to 1
    # NB: This is essential when comparing probs from different variants
    probs = probs - np.max(probs, axis=1)[:, np.newaxis]
    probs = probs - np.log(np.sum(np.exp(probs), axis=1))[:, np.newaxis]


    biallelic_id = 0
    n_changed = 0
    for multiallelic in multiallelic_map:
        if len(multiallelic) == 1:
            # no multiallelic, do nothing
            pass
        else:
            variant_probs = probs[biallelic_id:biallelic_id + len(multiallelic)]
            variant_genotypes = genotypes[biallelic_id:biallelic_id + len(multiallelic)]
            # find most likely non-0/0 genotype among all variants
            # if this variant is 0/0, set all genotypes to 0/0
            # if this is 1/1, set all others to 0/0
            # if this is 0/1, check if any others is 0/1 and keep those two as 0/1 and set rest to 0/0

            # find variant with highest prob on not 0/0 (1:)
            most_likely_non_homo_ref = np.argmax(np.max(variant_probs[:, 1:], axis=1), axis=0)
            g = variant_genotypes[most_likely_non_homo_ref]

            # this happens if the variant with highest prob of not 0/0 itself has highest prob of 0/0
            # then we can set all to 0/0
            if g == 0 or g == 1:
                genotypes[biallelic_id:biallelic_id+len(multiallelic)] = 1
                n_changed += 1
            elif g == 2 or g == 3:  # 1/1
                genotypes[biallelic_id:biallelic_id+len(multiallelic)] = 1
                genotypes[biallelic_id+most_likely_non_homo_ref] = g
                n_changed += 1
            else:
                assert False
            #elif g == 1:
            # check if any other variant is 1/1 or 0/1
            # keep that other variant as 0/1 and set all others to 0/0
            #    sorting = np.argsort(np.max(variant_probs, axis=1), axis=0)
            #    other = sorting[1]
            #    if genotypes[]
        biallelic_id += len(multiallelic)

    logging.info(f"Number of genotypes that were changed in postprocessing: {np.sum(genotypes != original_genotypes)}")
    return genotypes, probs
//  [python]
def postprocess_multiallelic_calls(genotypes: np.ndarray, multiallelic_map: MultiAllelicMap, probs: np.ndarray):
    """
    Postprocesses genotype calls (biallelic) by making them compatible with
    multi-allelic variants.
    Idea is that variants that are part of the same multi-allelic variant
    need to be compatible (i.e. not conflicting genotypes)
    Uses the genotype probabilities (log-scale) to find the most likely genotypes
    within multiallelic variants

    Genotypes are numeric:
     0 or 1: 0/0, 2: 1/1, 3: 0/1

     Probs is matrix of shape n_variants x 3 where each
     column corresponds to log e prob of genotypes
     (0/0, 0/1, 1/1)


    """
    assert len(genotypes) == len(multiallelic_map.ravel()), len(multiallelic_map.ravel())
    original_genotypes = genotypes.copy()

    # nan-probs will cause problem
    n_nan = np.sum(np.isnan(probs))
    logging.info(f"{n_nan} probs are nan. Setting them to 1/3 before postprocesing genotypes")
    probs[np.isnan(probs)] = np.log(1/3)

    # normalize probs so that they sum to 1
    # NB: This is essential when comparing probs from different variants
    probs = probs - scipy.special.logsumexp(probs, axis=1)[:, np.newaxis]

    biallelic_id = 0
    n_changed = 0
    for multiallelic in multiallelic_map:
        if len(multiallelic) == 1:
            # no multiallelic, do nothing
            pass
        else:
            variant_probs = probs[biallelic_id:biallelic_id + len(multiallelic)]
            variant_genotypes = genotypes[biallelic_id:biallelic_id + len(multiallelic)]
            # find most likely non-0/0 genotype among all variants
            # if this variant is 0/0, set all genotypes to 0/0
            # if this is 1/1, set all others to 0/0
            # if this is 0/1, check if any others is 0/1 and keep those two as 0/1 and set rest to 0/0

            # find variant with highest prob on not 0/0 (1:)
            most_likely_non_homo_ref = np.argmax(np.max(variant_probs[:, 1:], axis=1), axis=0)
            g = variant_genotypes[most_likely_non_homo_ref]

            # this happens if the variant with highest prob of not 0/0 itself has highest prob of 0/0
            # then we can set all to 0/0
            if g == 0 or g == 1:
                genotypes[biallelic_id:biallelic_id+len(multiallelic)] = 1
                n_changed += 1
            elif g == 2 or g == 3:  # 1/1
                genotypes[biallelic_id:biallelic_id+len(multiallelic)] = 1
                genotypes[biallelic_id+most_likely_non_homo_ref] = g
                n_changed += 1
            else:
                assert False
            #elif g == 1:
            # check if any other variant is 1/1 or 0/1
            # keep that other variant as 0/1 and set all others to 0/0
            #    sorting = np.argsort(np.max(variant_probs, axis=1), axis=0)
            #    other = sorting[1]
            #    if genotypes[]
        biallelic_id += len(multiallelic)

    logging.info(f"Number of genotypes that were changed in postprocessing: {np.sum(genotypes != original_genotypes)}")
    return genotypes, probs
//  [python]
def _logtrapzexp(y, dx, axis=-1):
    """Compute the logarithm of the integral of the exponential of y using the trapezoidal rule.

    The function works only with uniformly-spaced grid (with spacing dx).

    """
    n_samples = y.shape[axis]

    # Make sure axis is in the positive format.
    if axis < 0:
        axis = len(y.shape) + axis

    # Build weights for the logsumexp to implement trapezoidal rule.
    shape = [1] * axis + [n_samples] + [1] * (len(y.shape) - axis - 1)
    weights = np.ones(shape)
    weights[:, 0] = 0.5
    weights[:, -1] = 0.5
    weights *= dx

    # Perform exponential integral.
    return scipy.special.logsumexp(y, axis=axis, b=weights)
//  [python]
def _compute_bin_delta_f(minus_work, rbias, det_dcv=None):
    """Compute the Delta f(s) for a single bin given the list of work and MetaD bias values.

    If det_dcv is given, the contribution to the geometric Delta f(s) is computed as well.

    The free energy is returned in reduced units.
    """
    if len(minus_work) == 0:
        if det_dcv is None:
            return np.nan
        return np.nan, np.nan

    rbias_minus_work = rbias + minus_work
    logsumexp_rbias_minus_work = np.max(rbias_minus_work) + np.log(np.sum(np.exp(rbias_minus_work - np.max(rbias_minus_work))))
    delta_f = - (logsumexp_rbias_minus_work - np.max(rbias) - np.log(np.sum(np.exp(rbias - np.max(rbias)))))

    if det_dcv is not None:
        log_mean_det_dcv = np.max(rbias_minus_work + np.log(det_dcv)) + np.log(np.sum(np.exp(rbias_minus_work + np.log(det_dcv) - np.max(rbias_minus_work + np.log(det_dcv))))) - logsumexp_rbias_minus_work
        return delta_f, log_mean_det_dcv
    return delta_f
//  [python]
def _compute_bin_delta_f(minus_work, rbias, det_dcv=None):
    """Compute the Delta f(s) for a single bin given the list of work and MetaD bias values.

    If det_dcv is given, the contribution to the geometric Delta f(s) is computed as well.

    The free energy is returned in reduced units.
    """
    if len(minus_work) == 0:
        if det_dcv is None:
            return np.nan
        return np.nan, np.nan

    rbias_minus_work = rbias + minus_work
    logsumexp_rbias_minus_work = scipy.special.logsumexp(rbias_minus_work)
    delta_f = - (logsumexp_rbias_minus_work - scipy.special.logsumexp(rbias))

    if det_dcv is not None:
        log_mean_det_dcv = scipy.special.logsumexp(rbias_minus_work + np.log(det_dcv)) - logsumexp_rbias_minus_work
        return delta_f, log_mean_det_dcv
    return delta_f
//  [python]
def update_log_evidence(self):
        """Sum up all the model-specific evidences from the submodels"""
        self.log_evidence = np.log(np.sum(np.exp([model.model_log_evidence
                                for model in self.model_universe])))
//  [python]
def save_negative_log_likelihood(self,t):
        """Get the negative log likelihood if you want to store it"""
        #DEBUG: Unclear why model and run length log distr has different length
        #       Maybe because of trimming?
        all_one_step_ahead_lklhoods_weighted = [
            (self.model_universe[m].one_step_ahead_predictive_log_probs + 
             self.model_and_run_length_log_distr[m,
                        self.model_and_run_length_log_distr[m,:]> -np.inf])
             #self.model_universe[m].retained_run_lengths])
             #np.where(self.model_and_run_length_log_distr[m,:]> -np.inf)])            
             for m in range(0, self.Q)]
        summed_up = -np.log(np.sum(np.exp([item for entry in 
            all_one_step_ahead_lklhoods_weighted for item in entry])))
        self.negative_log_likelihood.append(summed_up)
//  [python]
def save_negative_log_likelihood(self,t):
        """Get the negative log likelihood if you want to store it"""
        #DEBUG: Unclear why model and run length log distr has different length
        #       Maybe because of trimming?
        all_one_step_ahead_lklhoods_weighted = [
            (self.model_universe[m].one_step_ahead_predictive_log_probs + 
             self.model_and_run_length_log_distr[m,
                        self.model_and_run_length_log_distr[m,:]> -np.inf])
             #self.model_universe[m].retained_run_lengths])
             #np.where(self.model_and_run_length_log_distr[m,:]> -np.inf)])            
             for m in range(0, self.Q)]
        summed_up = -special.logsumexp([item for entry in 
            all_one_step_ahead_lklhoods_weighted for item in entry])
        self.negative_log_likelihood.append(summed_up)
//  [python]
def matrix(self):
        return np.exp(self.logmat - s.logsumexp(self.logmat, axis=-1, keepdims=True))
//  [python]
def matrix(self):
        return np.exp(self.logmat - logsumexp(self.logmat, axis=-1, keepdims=True))
//  [python]
def save_negative_log_likelihood_fixed_pars(self, t):
        """For comparison with GP, fix sigma^2 at the MAP when doing the 
        one-step-ahead prediction."""
        all_one_step_ahead_lklhoods_weighted = [
            (self.model_universe[m].one_step_ahead_predictive_log_probs_fixed_pars + 
             self.model_and_run_length_log_distr[m,
                        self.model_and_run_length_log_distr[m,:]> -np.inf])
            for m in range(0, self.Q)]
             #self.model_universe[m].retained_run_lengths])
             #np.where(self.model_and_run_length_log_distr[m,:]> -np.inf)])            
             
        summed_up = -np.log(np.sum(np.exp([item for entry in 
            all_one_step_ahead_lklhoods_weighted for item in entry])))
        self.negative_log_likelihood_fixed_pars.append(summed_up)