code: def get_expected_obs(qs_pi, A):
      """
      Compute the expected observations under a policy, also known as the posterior predictive density over observations
  
      Parameters
      ----------
      qs_pi: ``list`` of ``numpy.ndarray`` of dtype object
          Predictive posterior beliefs over hidden states expected under the policy, where ``qs_pi[t]`` stores the beliefs about
          hidden states expected under the policy at time ``t``
      A: ``numpy.ndarray`` of dtype object
          Sensory likelihood mapping or 'observation model', mapping from hidden states to observations. Each element ``A[m]`` of
          stores an ``numpy.ndarray`` multidimensional array for observation modality ``m``, whose entries ``A[m][i, j, k, ...]`` store
          the probability of observation level ``i`` given hidden state levels ``j, k, ...``
  
      Returns
      -------
      qo_pi: ``list`` of ``numpy.ndarray`` of dtype object
          Predictive posterior beliefs over observations expected under the policy, where ``qo_pi[t]`` stores the beliefs about
          observations expected under the policy at time ``t``
      """
  
      n_steps = len(
          qs_pi
      )  # each element of the list is the PPD at a different timestep
  
      # initialise expected observations
      qo_pi = []
  
      for t in range(n_steps):
          qo_pi_t = utils.obj_array(len(A))
          qo_pi.append(qo_pi_t)
  
      # compute expected observations over time
      for t in range(n_steps):
          for modality, A_m in enumerate(A):
              qo_pi[t][modality] = spm_dot(A_m, qs_pi[t])
  
      return qo_pi
  
imports: import itertools
  import numpy
  from pymdp.maths import softmax, softmax_obj_arr, spm_dot, spm_wnorm, spm_MDP_G, spm_log_single, spm_log_obj_array
  from pymdp import utils
  import copy
label_desc: Use image node_id 4 for calling a global function with example usage: get_expected_obs(qs_pi, A) and returns: qo_pi