Projective Simulation
  • Get started
  • Documentation
  • Tutorials
  • Research
  1. Environments
  2. POMDPs
  • Documentation
  • Agents
    • Core
    • Foraging
  • ECMs
    • Core
    • Priming
    • State Estimators
  • Environments
    • Core
    • Foraging
    • POMDPs
    • Factorized POMDPs

On this page

  • POMDP Example
  • Prebuilt POMDPs
  • View source
  • Report an issue
  1. Environments
  2. POMDPs

POMDPs


source

POMDP

def POMDP(
    percepts:NDArray[numpy.int64], # An SxK array where S is the number of Percepts and K is the number of categories for each percept. If 1d, converted to Sx1
    observation_function:NDArray[numpy.float64], # An NxS array, where N is the number of states in the cycle and S is the number of possible percepts. Rows contain probability distributions
    transition_function:NDArray[numpy.float64], # An NxNxA array, where A is the number of actions. Rows in each slice contain probability distributions.
    initial_state:int=0, # Start state of POMDP
):

A general constructor for POMDPs

POMDP Example

Here we define a simple Markov Decision Process in which an agent must match one of two actions to the first category of its percept. If it does, the environment moves to a state that produces an observable reward. If it does, the environment moves to a state without reward. Note that this environment is fully observable - each state maps to a unique percept. The POMDP class is general and can handle such a case.

from projective_simulation.agents.core import Basic_2Layer_Agent
#Create a percept for each combination of signal and reward
percepts = np.array([[0,0],
                     [0,1],
                     [1,0],
                     [1,1]]
                   )
#Create observation function for 4-state environment, each selects producing one of the perceprs
observation_function = np.array([[1.,0.,0.,0.],
                                 [0.,1.,0.,0.],
                                 [0.,0.,1.,0.],
                                 [0.,0.,0.,1.]]                                
                               )
#Create transition function for 4-state, 2-action environment, where transitions are uniform-random to states with the appropriate reward
transition_function = np.array([[[0.,0.5], #in state 0, action 0 transition either to state 1 or 3. Action 1 transitions either to state 0 or 2.
                                 [0.5,0.],
                                 [0.,0.5],
                                 [0.5,0.]],
                                [[0.,0.5], #in state 1, action 0 transition either to state 1 or 3. Action 1 transitions either to state 0 or 2.
                                 [0.5,0.],
                                 [0.,0.5],
                                 [0.5,0.]],
                                [[0.5,0.], #in state 2, action 0 transition either to state 0 or 2. Action 1 transitions either to state 1 or 3.
                                 [0.,0.5],
                                 [0.5,0.],
                                 [0.,0.5]],
                                [[0.5,0.], #in state 3, action 0 transition either to state 0 or 2. Action 1 transitions either to state 1 or 3.
                                 [0.,0.5],
                                 [0.5,0.],
                                 [0.,0.5]]]
                              )
#Initialize POMDP
example_env = POMDP(percepts = percepts, observation_function = observation_function, transition_function = transition_function)

#Initialize Agent
test_agent = Basic_2Layer_Agent(num_actions = np.shape(transition_function)[2], glow = 1., policy = 'softmax', policy_parameters = 1)

#run percept action loop for 50 steps
T = 100
observed_percepts: list[None|NDArray[np.int_]] = [None] * T #empty list for storing percepts
actions: list[None|int] = [None] * T  #empty list for storing actions
for t in range(T):
    percept = example_env.get_observation()
    observed_percepts[t] = percept
    reward = percept[1]
    test_agent.update(reward)
    action = test_agent.deliberate(str(percept))
    actions[t] = action
    example_env.transition(action)


#show final 10 percepts and actions. Agent should have learned to select actions that produce rewards
for t in range(T-10,T):
    print(f'Step {t}: percept = {observed_percepts[t]}; action = {actions[t]}')
Step 90: percept = [1 1]; action = 1
Step 91: percept = [0 1]; action = 0
Step 92: percept = [0 1]; action = 0
Step 93: percept = [1 1]; action = 1
Step 94: percept = [1 1]; action = 1
Step 95: percept = [0 1]; action = 0
Step 96: percept = [1 1]; action = 1
Step 97: percept = [0 1]; action = 0
Step 98: percept = [1 1]; action = 1
Step 99: percept = [1 1]; action = 1

Prebuilt POMDPs


source

Cyclic_Env

def Cyclic_Env(
    percept_cycle:NDArray[numpy.int64], # an N x K array, where N is the number of states in the cycles and K is the number of categories in a percept. If 1d, will be converted to Nx1
    initial_state:int=0, # index of first percept returned by get_observation()
    supress_warning:bool=False, # warns if Action != 0 is input to transition function
):

An environment that cycles deterministically through a sequence of percepts that may be passed to an agent

Cyclic Environment Example

In this example, we set up a cyclical environment in which a light turns green, turns off, turns blue, turns off, and then repeats.

percept_cycle = np.array(["green", "off", "blue", "off"])
light_cycle_instance1 = Cyclic_Env(percept_cycle)
T = 8 #total time steps to simulate
observed_percepts:list[None|np.ndarray[tuple[int],np.dtype[np.int_]]] = [None] * T #data structure for storing observations

#simulate for T steps and store observations
for t in range(T):
    observed_percepts[t] = light_cycle_instance1.get_observation()
    light_cycle_instance1.transition(0) #use action 0 for transition

observed_percepts
[array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5')]

We can also choose to initiate anywhere in the cycle by giving the desired index. In this example, we start in State 2, which returns the “blue” percept.

light_cycle_instance2 = Cyclic_Env(percept_cycle, initial_state = 2)
T = 8 #total time steps to simulate
observed_percepts:list[None|np.ndarray[tuple[int],np.dtype[np.int_]]] = [None] * T #data structure for storing observations


#simulate for T steps and store observations
for t in range(T):
    observed_percepts[t] = light_cycle_instance2.get_observation()
    light_cycle_instance2.transition(0) #use action 0 for transition
    
observed_percepts
[array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5')]

Noisy Cycle


source

Noisy_Cycle

def Noisy_Cycle(
    percepts:NDArray[numpy.int64], # an SxK array where S is the number of Percepts and K is the number of categories for each percept
    observation_function:NDArray[numpy.float64], # An NxS array, where N is the number of states in the cycle and S is the number of possible percepts
    initial_state:int=0, supress_warning:bool=False, # warns if Action != 0 is input to transition function
):

A general constructor for POMDPs

Noisy Cycle Example
percepts = np.array(["green","off","blue"])
observation_function = np.array([[0.9,0.,0.1],
                                 [0.,1.,0.],
                                 [0.1,0.,0.9],
                                 [0.,1.,0.]])
noisy_light_cycle = Noisy_Cycle(percepts, observation_function)
T = 20 #total time steps to simulate
observed_percepts:list[NDArray[np.int_]|None] = [None] * T #data structure for storing observations

#simulate for T steps and store observations
for t in range(T):
    observed_percepts[t] = noisy_light_cycle.get_observation()
    noisy_light_cycle.transition(0) #use action 0

observed_percepts
[array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['green'], dtype='<U5'),
 array(['off'], dtype='<U5'),
 array(['blue'], dtype='<U5'),
 array(['off'], dtype='<U5')]
  • View source
  • Report an issue