docstr: Test the states_info_gain function. 
  Function is tested by manipulating uncertainty in the likelihood matrices (A or B)
  in a ways that alternatively change the resolvability of uncertainty
  This is done with A) an imprecise expected state and a precise sensory mapping, 
  and B) high ambiguity and imprecise sensory mapping.
docstr_tok: ["Test","the","states_info_gain","function",".","Function","is","tested","by","manipulating","uncertainty","in","the","likelihood","matrices","(","A","or","B",")","in","a","ways","that","alternatively","change","the","resolvability","of","uncertainty","This","is","done","with","A",")","an","imprecise","expected","state","and","a","precise","sensory","mapping",",","and","B",")","high","ambiguity","and","imprecise","sensory","mapping","."]
code: def test_state_info_gain(self):
      """
      Test the states_info_gain function.
      Function is tested by manipulating uncertainty in the likelihood matrices (A or B)
      in a ways that alternatively change the resolvability of uncertainty
      This is done with A) an imprecise expected state and a precise sensory mapping,
      and B) high ambiguity and imprecise sensory mapping.
      """
  
      num_states = [2]
      num_controls = [2]
  
      # start with a precise initial state
      qs = utils.to_obj_array(utils.onehot(0, num_states[0]))
  
      """Case study 1: Uncertain states, unambiguous observations"""
      # add some uncertainty into the consequences of the second policy, which
      # leads to increased epistemic value of observations, in case of pursuing
      # that policy -- this of course depends on a precise observation likelihood model
      B = utils.construct_controllable_B(num_states, num_controls)
      B[0][:, :, 1] = maths.softmax(
          B[0][:, :, 1]
      )  # "noise-ify" the consequences of the 1-th action
  
      # single timestep
      n_step = 1
      policies = control.construct_policies(
          num_states, num_controls, policy_len=n_step
      )
  
      # single observation modality
      num_obs = [2]
  
      # create noiseless identity A matrix
      A = utils.to_obj_array(np.eye(num_obs[0]))
  
      state_info_gains = np.zeros(
          len(policies)
      )  # store the Bayesian surprise / epistemic values of states here (AKA state info gain)
      for idx, policy in enumerate(policies):
          qs_pi = control.get_expected_states(qs, B, policy)
          state_info_gains[idx] += control.calc_states_info_gain(
              A, qs_pi
          )
      self.assertGreater(state_info_gains[1], state_info_gains[0])
  
      """Case study 2: Uncertain states, ambiguous observations (for all states)"""
      # now we 'undo' the epistemic bonus of the second policy by making the A matrix
      # totally ambiguous; thus observations cannot resolve uncertainty about hidden states.
      # In this case, uncertainty in the posterior beliefs induced by Policy 1 doesn't tip the balance
      # of epistemic value, because uncertainty is irresolveable either way.
      A = utils.obj_array_uniform([[num_obs[0], num_states[0]]])
  
      state_info_gains = np.zeros(len(policies))
      for idx, policy in enumerate(policies):
          qs_pi = control.get_expected_states(qs, B, policy)
          state_info_gains[idx] += control.calc_states_info_gain(
              A, qs_pi
          )
      self.assertEqual(state_info_gains[0], state_info_gains[1])
  
      """Case study 2: Uncertain states, ambiguous observations (for particular states)"""
  
      # create noiseless identity A matrix
      A = utils.to_obj_array(np.eye(num_obs[0]))
  
      # add some uncertainty into the consequences of the both policies
      B = utils.construct_controllable_B(num_states, num_controls)
  
      B[0][:, :, 0] = maths.softmax(
          B[0][:, :, 0] * 2.0
      )  # "noise-ify" the consequences of the 0-th action, but to a lesser extent than the 1-th action
      B[0][:, :, 1] = maths.softmax(
          B[0][:, :, 1]
      )  # "noise-ify" the consequences of the 1-th action
  
      # Although in the presence of a precise likelihood mapping,
      # Policy 1 would be preferred (due to higher resolve-able uncertainty, introduced by a noisier action-dependent B matrix),
      # if the expected observation likelihood of being in state 1 (the most likely consequence of Policy 1) is not precise, then
      # Policy 0 (which has more probability loaded over state 0) will have more resolveable uncertainty, due to the
      # higher precision of the A matrix over that column (column 0, which is identity). Even though the expected density over states
      # is less noisy for policy 0.
      A[0][:, 1] = maths.softmax(A[0][:, 1])
  
      state_info_gains = np.zeros(len(policies))
      for idx, policy in enumerate(policies):
          qs_pi = control.get_expected_states(qs, B, policy)
          state_info_gains[idx] += control.calc_states_info_gain(
              A, qs_pi
          )
      self.assertGreater(state_info_gains[1], state_info_gains[0])
  
imports: import os
  import unittest
  import numpy
  from pymdp import utils, maths
  from pymdp import control
label_desc: Use image node_id 4 for calling the TestControl obj's underlying member method code with example usage: obj.test_state_info_gain() without return types