A generator that iterates through all parameters requested the
        correct number of times each. Returns them as a dict with form
        {'param_name': param_value, ... } for use as f(**params).

def params(self):
        """A generator that iterates through all parameters requested the
        correct number of times each. Returns them as a dict with form
        {'param_name': param_value, ... } for use as f(**params)."""
        comb_sizes = []
        comb_keys = []
        for key,val in self.comb_params.items():
            comb_keys.append(key)
            comb_sizes.append(len(val))
        # length of first values array, since lengths should match for sets of
        # jointly varying parameters
        joint_sizes = [len(next(iter(jlist.values()))) for jlist in self.joint_params]
        all_sizes = comb_sizes + joint_sizes
        total_combinations = reduce(operator.mul, all_sizes, 1)
        for i in range(total_combinations):
            sub = np.unravel_index(i, all_sizes)
            params = {}
            # get the comb_params
            for j in range(len(comb_sizes)):
                key = comb_keys[j]
                val_arr = self.comb_params[key]
                params[key] = val_arr[sub[j]]
            # get the joint_params
            for j in range(len(joint_sizes)):
                subj = j + len(comb_sizes)
                for key,val_arr in self.joint_params[j].items():
                    params[key] = val_arr[sub[subj]]
            # how we have one parameter set, check how many times to repeat it
            num_repeats = self.default_repeats
            for func in self.count_funcs:
                c = func(params)
                if c:
                    num_repeats = c
            for i in range(num_repeats):
                yield params