Return a simplification that converts the positional argument
    `arg_index` from (str, int) to a subclass of :class:`.LocalSpace`, as well
    as any keyword argument with one of the given keys.

    The exact type of the resulting Hilbert space is determined by
    the `default_hs_cls` argument of :func:`init_algebra`.

    In many cases, we have :func:`implied_local_space` (in ``create``) in
    addition to a conversion in ``__init__``, so
    that :func:`match_replace` etc can rely on the relevant arguments being a
    :class:`HilbertSpace` instance.

def implied_local_space(*, arg_index=None, keys=None):
    """Return a simplification that converts the positional argument
    `arg_index` from (str, int) to a subclass of :class:`.LocalSpace`, as well
    as any keyword argument with one of the given keys.

    The exact type of the resulting Hilbert space is determined by
    the `default_hs_cls` argument of :func:`init_algebra`.

    In many cases, we have :func:`implied_local_space` (in ``create``) in
    addition to a conversion in ``__init__``, so
    that :func:`match_replace` etc can rely on the relevant arguments being a
    :class:`HilbertSpace` instance.
    """
    from qnet.algebra.core.hilbert_space_algebra import (
        HilbertSpace, LocalSpace)

    def args_to_local_space(cls, args, kwargs):
        """Convert (str, int) of selected args to :class:`.LocalSpace`"""
        if isinstance(args[arg_index], LocalSpace):
            new_args = args
        else:
            if isinstance(args[arg_index], (int, str)):
                try:
                    hs = cls._default_hs_cls(args[arg_index])
                except AttributeError:
                    hs = LocalSpace(args[arg_index])
            else:
                hs = args[arg_index]
                assert isinstance(hs, HilbertSpace)
            new_args = (tuple(args[:arg_index]) + (hs,) +
                        tuple(args[arg_index + 1:]))
        return new_args, kwargs

    def kwargs_to_local_space(cls, args, kwargs):
        """Convert (str, int) of selected kwargs to LocalSpace"""
        if all([isinstance(kwargs[key], LocalSpace) for key in keys]):
            new_kwargs = kwargs
        else:
            new_kwargs = {}
            for key, val in kwargs.items():
                if key in keys:
                    if isinstance(val, (int, str)):
                        try:
                            val = cls._default_hs_cls(val)
                        except AttributeError:
                            val = LocalSpace(val)
                    assert isinstance(val, HilbertSpace)
                new_kwargs[key] = val
        return args, new_kwargs

    def to_local_space(cls, args, kwargs):
        """Convert (str, int) of selected args and kwargs to LocalSpace"""
        new_args, __ = args_to_local_space(args, kwargs, arg_index)
        __, new_kwargs = kwargs_to_local_space(args, kwargs, keys)
        return new_args, new_kwargs

    if (arg_index is not None) and (keys is None):
        return args_to_local_space
    elif (arg_index is None) and (keys is not None):
        return kwargs_to_local_space
    elif (arg_index is not None) and (keys is not None):
        return to_local_space
    else:
        raise ValueError("must give at least one of arg_index and keys")