Elegantly create Dicts of parameters

question

#1

Hey there,

I’m trying to write a script that creates dictionaries with simulation parameters.
I need these to eventually call
pmap(job_function, parameter_dicts)
where each worker gets its own parameter set and does its thing.

My question is how to elegantly create these dictionaries.
So far the only way I’ve been able to come up with is this:

# assign all parameters (optionally Vector of values)
param1 = [1,2]
param2 = 5
param3 = [4,5]
....
param20 = [10,12,15]

parameter_dicts = Dict[]

# Iterate over all possible parameter combinations
for param1 ∈ param1, 
    param2 ∈ param2,
    param3 ∈ param3, 
    ... , 
    param20 ∈ param20
   #Create dict with the parameters
   d = Dict( :param1 => param1,
                  :param2 => param2,
                  :param3 => param3,
                  ...
                  :param20 => param20)
  push!(parameter_dicts, d)
end

which is obviously a very long and unreadable script.
Suggestions would be welcome!


#2

Hi,
to do the same thing you’re doing with the loop somewhat more concise, you can do

# assign all parameters (optionally Vector of values)
param1 = [1,2]
param2 = 5
param3 = [4,5]
....
param20 = [10,12,15]
params = [parm1, param2, param3, ..., param20]
paramnames = (:param1, :param2, :param3, ... , :param20)
 
parameterdicts = [Dict((paramnames .=> params)) for params in Iterators.product(params...)]

Here, Iterators.product gives you all possible combinations of the arguments you provide.
I also use broadcasting to combine parameter-names and their values into pairs.
This is the same approach you use but (possibly) a bit more concise.

To organise parameters I’d take a look at the package https://github.com/mauro3/Parameters.jl which provides structs with default values to save parameters.

I’m also not sure about the use of dictionaries in this case, since depending on your params (e.g. having some Int and Strings), the dictionary will have Any value type, that is, if you access it, the compiler doesn’t know what type it returns. That might be an issue.

Instead you could use NamedTuples, which carry type information about all their fields.
To use that instead, you’d have to change the dictionary part above to:

NamedTuple{paramnames}(params) 

Maybe worth a quick test-run.


#3

Have you considered:


#4

Thank you for the inspiration!
Iterators.product did the trick.

Type stability is really not a problem in this case because all computations happen
behind additional function barriers.

This seems like a good solution:

pset = Dict(
   param1 = [1,2],
   param2 = 5,
   param3 = [4,5])

map(vals -> Dict(keys(pset) .=> vals), Iterators.product(values(pset)...))[:]