Skip to content

Commit

Permalink
Checkin to preserve work. Has not been parsed, much less run/tested!!!
Browse files Browse the repository at this point in the history
  • Loading branch information
dcunning11235 committed Feb 22, 2019
1 parent 7ad9747 commit 0d06a4b
Showing 1 changed file with 124 additions and 40 deletions.
164 changes: 124 additions & 40 deletions mesa/batchrunner.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from itertools import product, count
import pandas as pd
from tqdm import tqdm

import random

try:
from pathos.multiprocessing import ProcessPool
except ImportError:
Expand All @@ -18,18 +21,25 @@
pathos_support = True


class VariableParameterError(TypeError):
MESSAGE = ('variable_parameters must map a name to a sequence of values. '
'These parameters were given with non-sequence values: {}')
class ParameterError(TypeError):
MESSAGE = ('parameters must map a name to a value. '
'These names did not match paramerets: {}')

def __init__(self, bad_names):
self.bad_names = bad_names

def __str__(self):
return self.MESSAGE.format(self.bad_names)

class VariableParameterError(ParameterError):
MESSAGE = ('variable_parameters must map a name to a sequence of values. '
'These parameters were given with non-sequence values: {}')

def __init__(self, bad_names):
super().__init__(bad_names)

class BatchRunner:

class FixedBatchRunner:
""" This class is instantiated with a model class, and model parameters
associated with one or more values. It is also instantiated with model and
agent-level reporters, dictionaries mapping a variable name to a function
Expand All @@ -39,9 +49,8 @@ class BatchRunner:
Note that by default, the reporters only collect data at the *end* of the
run. To get step by step data, simply have a reporter store the model's
entire DataCollector object.
"""
def __init__(self, model_cls, variable_parameters=None,
def __init__(self, model_cls, parameters_list=None,
fixed_parameters=None, iterations=1, max_steps=1000,
model_reporters=None, agent_reporters=None,
display_progress=True):
Expand All @@ -50,20 +59,20 @@ def __init__(self, model_cls, variable_parameters=None,
Args:
model_cls: The class of model to batch-run.
variable_parameters: Dictionary of parameters to lists of values.
The model will be run with every combo of these paramters.
For example, given variable_parameters of
{"param_1": range(5),
"param_2": [1, 5, 10]}
models will be run with {param_1=1, param_2=1},
{param_1=2, param_2=1}, ..., {param_1=4, param_2=10}.
parameters_list: A list of dictionaries of parameter sets.
The model will be run with dictionary of paramters.
For example, given parameters_list of
[{"homophily": 3, "density": 0.8, "minority_pc": 0.2},
{"homophily": 2, "density": 0.9, "minority_pc": 0.1},
{"homophily": 4, "density": 0.6, "minority_pc": 0.5}]
3 models will be run, one for each provided set of parameters.
fixed_parameters: Dictionary of parameters that stay same through
all batch runs. For example, given fixed_parameters of
{"constant_parameter": 3},
every instantiated model will be passed constant_parameter=3
as a kwarg.
iterations: The total number of times to run the model for each
combination of parameters.
iterations: The total number of times to run the model for each set
of parameters.
max_steps: Upper limit of steps above which each run will be halted
if it hasn't halted on its own.
model_reporters: The dictionary of variables to collect on each run
Expand All @@ -77,9 +86,9 @@ def __init__(self, model_cls, variable_parameters=None,
"""
self.model_cls = model_cls
if variable_parameters is None:
variable_parameters = {}
self.variable_parameters = self._process_parameters(variable_parameters)
if parameters_list is None:
parameters_list = []
self.parameters_list = parameters_list
self.fixed_parameters = fixed_parameters or {}
self._include_fixed = len(self.fixed_parameters.keys()) > 0
self.iterations = iterations
Expand All @@ -96,16 +105,6 @@ def __init__(self, model_cls, variable_parameters=None,

self.display_progress = display_progress

def _process_parameters(self, params):
params = copy.deepcopy(params)
bad_names = []
for name, values in params.items():
if (isinstance(values, str) or not hasattr(values, "__iter__")):
bad_names.append(name)
if bad_names:
raise VariableParameterError(bad_names)
return params

def _make_model_args(self):
"""Prepare all combinations of parameter values for `run_all`
Expand All @@ -117,21 +116,19 @@ def _make_model_args(self):
all_kwargs = []
all_param_values = []

if len(self.variable_parameters) > 0:
param_names, param_ranges = zip(*self.variable_parameters.items())
for param_range in param_ranges:
total_iterations *= len(param_range)

for param_values in product(*param_ranges):
kwargs = dict(zip(param_names, param_values))
if len(self.parameters_list):
total_iterations *= len(self.parameters_list)
for params in self.parameters_list:
kwargs = params.copy()
kwargs.update(self.fixed_parameters)
all_kwargs.append(kwargs)
all_param_values.append(param_values)
all_param_values.append(params.values())
elif len(self.fixed_parameters):
kwargs = self.fixed_parameters.copy()
all_kwargs.append(kwargs)
all_param_values.append(kwargs.values())
else:
kwargs = self.fixed_parameters
param_values = None
all_kwargs = [kwargs]
all_param_values = [None]
total_iterations = 0

return (total_iterations, all_kwargs, all_param_values)

Expand Down Expand Up @@ -237,6 +234,93 @@ def _prepare_report_table(self, vars_dict, extra_cols=None):
return ordered


#This is kind of a useless class, but it does carry the 'source' parameters with it
class ParameterProduct:
def __init__(self, variable_parameters):
self.param_names, self.param_lists = \
zip( *(copy.deepcopy(variable_parameters)).items() )
self._product = product(*self.param_lists)

def __iter__(self):
return self

def next(self):
return dict(zip(self.param_names, self._product.next()))

#Roughly inspired by sklearn.model_selection.ParameterSampler. Does not handle
#distributions, only lists.
class ParameterSampler:
def __init__(self, parameter_lists, n, random_state=None):
self.param_names, self.param_lists = \
zip( *(copy.deepcopy(variable_parameters)).items() )
self.n = n
if random_state is None:
self.random_state = random.Random()
elif isinstance(random_state, int):
self.random_state = random.Random(random_state)
else:
self.random_state = random_state

def __iter__(self):
return self

def next(self):
return dict(zip(self.param_names, [self.random_state.choose(l) for l in self.param_lists]))


class BatchRunner(FixedBatchRunner):
""" This class is instantiated with a model class, and model parameters
associated with one or more values. It is also instantiated with model and
agent-level reporters, dictionaries mapping a variable name to a function
which collects some data from the model or its agents at the end of the run
and stores it.
Note that by default, the reporters only collect data at the *end* of the
run. To get step by step data, simply have a reporter store the model's
entire DataCollector object.
"""
def __init__(self, model_cls, variable_parameters=None,
fixed_parameters=None, iterations=1, max_steps=1000,
model_reporters=None, agent_reporters=None,
display_progress=True):
""" Create a new BatchRunner for a given model with the given
parameters.
Args:
model_cls: The class of model to batch-run.
variable_parameters: Dictionary of parameters to lists of values.
The model will be run with every combo of these paramters.
For example, given variable_parameters of
{"param_1": range(5),
"param_2": [1, 5, 10]}
models will be run with {param_1=1, param_2=1},
{param_1=2, param_2=1}, ..., {param_1=4, param_2=10}.
fixed_parameters: Dictionary of parameters that stay same through
all batch runs. For example, given fixed_parameters of
{"constant_parameter": 3},
every instantiated model will be passed constant_parameter=3
as a kwarg.
iterations: The total number of times to run the model for each
combination of parameters.
max_steps: Upper limit of steps above which each run will be halted
if it hasn't halted on its own.
model_reporters: The dictionary of variables to collect on each run
at the end, with variable names mapped to a function to collect
them. For example:
{"agent_count": lambda m: m.schedule.get_agent_count()}
agent_reporters: Like model_reporters, but each variable is now
collected at the level of each agent present in the model at
the end of the run.
display_progress: Display progresss bar with time estimation?
"""
super().__init__(model_cls, ParameterProduct(variable_parameters),
fixed_parameters, iterations, max_steps,
model_reporters, agent_reporters,
display_progress)


class MPSupport(Exception):
def __str__(self):
return ("BatchRunnerMP depends on pathos, which is either not "
Expand Down

0 comments on commit 0d06a4b

Please sign in to comment.