Getting an Error when referencing params

I’m getting an error I can’t figure out when using sweepable simulation parameters. I’m working off the Robots and Marbles tutorial. Just tried to add a parameter and I’m getting a type error: TypeError: list indices must be integers or slices, not str. So I think when it reads the params input it’s reading it as a list instead of a dict… I’m not getting the same issue for the s dict even though it looks like it’s structured the same way to me. And I don’t see any differences between how I’m defining the parameters and the documentation, but I could very well be missing something. Here’s the code:

indent preformatted text by 4 spaces
initial_conditions = {
'box_A': 20, # as per the description of the example, box_A starts out with 10 marbles in it
'box_B': 0 # as per the description of the example, box_B starts out empty
}

%%capture
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #    
# We specify the robot arm's logic in a Policy Function    

def robot_arm1(params, step, sL, s):
    add_to_A2 = 0
if (s['box_A'] > s['box_B']):
    add_to_A2 = -1*params['multiplier']
elif (s['box_A'] < s['box_B']):
    add_to_A2 = 1
return({'add_to_A': add_to_A2, 'add_to_B': -add_to_A2})

def robot_arm2(params, step, sL, s):
add_to_A2 = 0
if (s['box_A'] > s['box_B']):
    add_to_A2 = -1
elif (s['box_A'] < s['box_B']):
    add_to_A2 = 1
return({'add_to_A': add_to_A2, 'add_to_B': -add_to_A2})

def changes(params, step, sL, s):
    x=step
    return({})


# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# We make the state update functions less "intelligent",
# ie. they simply add the number of marbles specified in _input 
# (which, per the policy function definition, may be negative)
def increment_A(params, step, sL, s, _input):
    y = 'box_A'
    x = s['box_A'] + _input['add_to_A']
    print("_input: ",_input)
    return (y, x)

def increment_B(params, step, sL, s, _input):
    y = 'box_B'
    x = s['box_B'] + _input['add_to_B']
    return (y, x)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# In the Partial State Update Blocks, 
# the user specifies if state update functions will be run in series or in parallel
# and the policy functions that will be evaluated in that block
partial_state_update_blocks = [
    { 
        'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions
        'robot_armA': robot_arm1,
        'robot_arm': robot_arm2,
        'changeThings': changes
    },
    'states': { # The following state variables will be updated simultaneously
        'box_A': increment_A,
        'box_B': increment_B
    }
}

]
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Settings of general simulation parameters, unrelated to the system itself
# T is a range with the number of discrete units of time the simulation will run for;
# N is the number of times the simulation will be run (Monte Carlo runs)
# In this example, we’ll run the simulation once (N=1) and its duration will be of 10 timesteps
# We’ll cover the M key in a future article. For now, let’s leave it empty

simulation_parameters = {
    'T': range(10),
    'N': 1,
    'M': {
        'multiplier': [2]
    }
}
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
from cadCAD.configuration import Configuration
 

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# The configurations above are then packaged into a `Configuration` object
config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial 
values
                       partial_state_update_blocks=partial_state_update_blocks, #dict containing state 
update functions
                   sim_config=simulation_parameters #dict containing simulation parameters
                  )
%%capture
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
exec_mode = ExecutionMode()
exec_context = ExecutionContext(exec_mode.single_proc)
executor = Executor(exec_context, [config]) # Pass the configuration object inside an array
raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements 
contains the raw results

–TypeError: list indices must be integers or slices, not str

In order to do parameter sweeps you need to sort of “pre-process” your simulation_parameters object. See how config_sim is called in the documentation.

Also, I suggest you use append_configs, as in these examples [1][2], instead of directly instantiating a Configuration object. append_configs takes care of some of that “pre-processing” for you too.

I see you’re referencing exec_mode.single_proc in your code. As you might have seen in the examples above, you need to use exec_mode. multi_proc when doing parameter sweeps, same as with A/B tests.

Hope that helps :slight_smile: