3

Let's say I have a Simulation object whose core attribute is a dictionary of parameters that takes something like the following form:

@pytest.fixture
def param_base():
    '''Dict of parameter defaults'''

    return {
    "fs" : 2e4,                 
    "sweep_length" : 1,         
    "num_trials" : 300,         
    ...
    "pool_tau" : 1.00,          
    "quantal_size" : -10,       
    "a_tau" : (0.001,0.005)
    }

I would like to write a pytest function that simply runs this simulation with a range of values for each of these parameters. A slightly differently structured dictionary can encapsulate this idea:

@pytest.fixture
def param_ranges():
    '''Dict of parameter ranges'''

    p_name = [
    "cav_p_open",
    "num_trials",
    "num_stim",
    "num_cav",
    "cav_i",
    "num_cav_ratio",
    "vesicle_prox",
    ]
    p_sets = [
    [0,0.01,0.99,1],   #cav_p_open
    [1,10,300],   #num_trials
    [1,2,5],  #num_stim
    [1,3,10],    #num_cav
    [0,1,5,10],  #cav_i
    [1,2],    #num_cav_ratio
    [0,0.01,0.25,1],    #vesicle_prox
    ]

    return dict(zip(p_name,p_sets))

Importantly, I do NOT want to run all combinations of all of these parameters, as the number of simulations grows far too quickly. I only want to vary one parameter at a time, while leaving the other parameters at their default values.

My current solution is as follows (continued after the above code):

parameter_names = [
"cav_p_open",
"num_trials",
"num_stim",
"num_cav",
"cav_i",
"num_cav_ratio",
"vesicle_prox",
]

@pytest.mark.parametrize("p_name", parameter_names)
def test_runModel_range_params(p_name,param_ranges,param_base):

    alt_params = copy.deepcopy(param_base)
    p_range = param_ranges[p_name]

    for i in range(len(p_range)):
        alt_params[p_name] = p_range[i]
        SIM = utils.Simulation(params = alt_params)

Which works pretty well, but because I'm looping through each parameter range, I can only see if the code fails because utils.Simulation failed at some value of a particular parameter, without knowing which one it failed on specifically.

So I think what I'm looking for is something like a nested version of pytest.mark.parameterize where I can run test_runModel_range_params on each of the range values for each parameter.

Any ideas? Extra points for elegance!

user3088305
  • 41
  • 1
  • 3

1 Answers1

1

I think what you are looking for is stacked parametrization. From the docs:

To get all combinations of multiple parametrized arguments you can stack parametrize decorators:

import pytest
@pytest.mark.parametrize("control_var1, control_var2", [(0, 1), ('b','a')])
@pytest.mark.parametrize("default_var1, default_var2", [(2, 3), ('b','a')])
def test_foo(control_var1, control_var2, default_var1, default_var2):
    pass
SilentGuy
  • 1,867
  • 17
  • 27