d.set(...) yields a copy of the IMap object d; the ... may be replaced with either
        nothing (in which case d is returned) or a list of 0 or more dictionaries followed by a lsit
        of zero or more keyword arguments. These dictionaries and keywords arguments are merged
        left-to-right; the result may contain only afferent parameters of d and replaces the values
        of d in the newly returned calc dictionary.

def set(self, *args, **kwargs):
        '''
        d.set(...) yields a copy of the IMap object d; the ... may be replaced with either
        nothing (in which case d is returned) or a list of 0 or more dictionaries followed by a lsit
        of zero or more keyword arguments. These dictionaries and keywords arguments are merged
        left-to-right; the result may contain only afferent parameters of d and replaces the values
        of d in the newly returned calc dictionary.
        '''
        args = merge(args, kwargs)
        if len(args) == 0: return self
        affs = self.afferents
        pln = self.plan
        # make sure these are all valid parameters
        if any(k not in affs for k in six.iterkeys(args)):
            raise TypeError(
                'The given key \'%s\' is not an afferent parameter of IMap object')
        # okay, we can make the change...
        new_calc_dict = copy.copy(self)
        new_affs = reduce(lambda m,v: m.set(v[0],v[1]), six.iteritems(args), affs)
        object.__setattr__(new_calc_dict, 'afferents', new_affs)
        # we need to run checks and delete any cache that has been invalidated.
        # The calculation's check method does this; it raises an exception if there is an error
        pln._check(new_calc_dict, changes=args)
        return new_calc_dict