Example #1
0
def describe(v):
    """
    Provides a single method, `describe`, for producing a unique and
    reproducible description of a variable.
    Description of sequences is intentionally not a one-to-one. From the point
    of view of parameters, all sequences are the same: they are either sequences
    of values we iterate over, or arrays used in vector operations. Both these
    uses are supported by by `ndarray`, so we treate sequences as follows:
        - For description (this function), all sequences are converted lists.
          This has a clean and compact string representation which is compatible
          with JSON.
        - When interpreting saved parameters, all sequences (which should all
          be lists) are converted to `ndarray`.
    Similarly, all mappings are converted to `ParameterSet`.

    This function is essentially one big if-else statement.
    """
    if isinstance(v, PlainArg) or v is None:
        return v
    elif isinstance(v, Sequence):
        r = [describe(u) for u in v]
        # OK, so it seems that Sumatra is ok with lists of dicts, but I leave
        # this here in case I need it later. Goes with "arg" test for Mappings
        # if not all(isinstance(u, PlainArg+(list,)) for u in r):
        #     # Sumatra only supports (nested) lists of plain args
        #     # -> Convert the list into a ParameterSet
        #     r = ParameterSet({f'arg{i}': u for i,u in enumerate(r)})
        return r
    elif isinstance(v, np.ndarray):
        return v.tolist()
    elif isinstance(v, Mapping):  # Covers ParameterSetBase
        # I think Sumatra only supports strings as keys
        r = ParameterSet({str(k): describe(u) for k, u in v.items()})
        for k in r.keys():
            # if k[:3].lower() == "arg":
            #     warn(f"Mapping keys beginning with 'arg', such as {k}, "
            #          "are reserved by `smttask`.")
            #     break
            if k.lower() == "type":
                warn("The mapping key 'type' is reserved by Sumatra and will "
                     "prevent the web interface from displaying the "
                     "parameters.")
        return r
    elif isinstance(v, Iterable):
        warn(f"Attempting to describe an iterable of type {type(v)}. Only "
             "Sequences (list, tuple) and ndarrays are properly supported.")
        return v
    # elif isinstance(v, File):
    #     return v.desc
    elif isinstance(v, DataFile):
        return File.get_desc(v.full_path)
    # elif isinstance(v, (Task, StatelessFunction, File)):
    elif hasattr(v, 'desc'):
        return v.desc
    elif isinstance(v, type):
        s = repr(v)
        if '<locals>' in s:
            warn(
                f"Type {s} is dynamically generated and thus not reproducible."
            )
        return s

    # scipy.stats Distribution types
    # elif isinstance(v,
    #     (_mv.multi_rv_generic, _mv.multi_rv_frozen)):
    #     if isinstance(v, _mv.multivariate_normal_gen):
    #         return "multivariate_normal"
    #     elif isinstance(v, _mv.multivariate_normal_frozen):
    #         return f"multivariate_normal(mean={v.mean()}, cov={v.cov()})"
    #     else:
    #         warn(dist_warning.format(type(v)))
    #         return repr(v)
    # elif isinstance(v, _mv.multi_rv_frozen):
    #     if isinstance(v, _mv.multivariate_normal_gen):
    #         return f"multivariate_normal)"
    #     else:
    #         warn(dist_warning.format(type(v)))
    #         return repr(v)

    else:
        warn("Task was not tested on inputs of type {}. "
             "Please make sure task digests are unique "
             "and reproducible.".format(type(v)))
        return repr(v)