예제 #1
0
def check_true_int_param(
    index: int,
    elems: Sequence[Any],
    param: str,
    func: str,
) -> int:
    """ Parse an integer parameter that isn't allowed to be a distribution. """

    try:
        value = elems[index]
    except IndexError:
        raise DistParseError(
            f"Missing parameter <{param}> for function '{func}'. "
            f"You provided {elems}.")

    if isinstance(value, float):
        raise DistParseError(
            f"Parameter <{param}> value '{value}' in function '{func}' "
            "cannot be a float.")

    try:
        return int(value)
    except (ValueError, TypeError):
        raise DistParseError(
            f"Could not parse parameter <{param}> value '{elems[index]}' "
            f"as an integer in function '{func}'.")
예제 #2
0
def check_str_param(index: int, elems: Sequence[Any], name: str,
                    func: str) -> Distribution[str]:
    """ Does error handling and conversion of str parameters. """

    try:
        param = parse_str_dist(elems[index])
    except IndexError:
        raise DistParseError(
            f"Missing parameter <{name}> for function '{func}'. "
            f"You provided {elems}.")
    except ValueError:
        raise DistParseError(
            f"Could not parse parameter <{name}> value '{elems[index]}' "
            f"as a categorical distribution for function '{func}'.")
    return param
예제 #3
0
def parse_neg_binomial(elems: Sequence[Any]) -> dist.NegBinomial:
    """ Parse a list definition as a negative binomial distribution definition.

    Examples:

    >>> r = parse_neg_binomial([5, 0.4, 2])
    >>> r.n
    IntConst(5)

    >>> r.p
    FloatConst(0.4)

    >>> r.loc
    IntConst(2)
    """

    str_func = "nbinomial"

    check_n_params(["n", "p", "loc"], elems, str_func)

    n = check_int_param(0, elems, "n", str_func)
    p = check_float_param(1, elems, "p", str_func)

    if not isinstance(p, dist.PDF):
        raise DistParseError(
            "Parameter <p> to nbinomial must be a float between 0 and 1. "
            "You provided '{elems[1]}'.")

    loc = check_int_param(2, elems, "loc", str_func)

    return dist.NegBinomial(n, p, loc)
예제 #4
0
def parse_string_as_int(elem: Union[int, str]) -> Distribution[int]:
    try:
        parsed_elem: Distribution[int] = dist.IntConst(int(elem))
    except ValueError:
        raise DistParseError(f"Could not parse value '{elem}' as an integer.")

    return parsed_elem
예제 #5
0
def parse_int_list(elems: Sequence[Any]) -> AOList[int]:
    """ Parse a list of integer distributions.

    Examples:
    >>> parse_int_list([1, 2, 3])
    AOList([IntConst(1), IntConst(2), IntConst(3)])

    >>> parse_int_list(["sample", 5, ["range", 1, 10]])
    Sample(5, Range(IntConst(1), IntConst(10)))

    >>> parse_int_list(["monotonic", 5, ["range", 1, 10]])
    Monotonic(5, Range(IntConst(1), IntConst(10)))

    >>> parse_int_list(["seq", 5, ["range", 1, 10]])
    Seq(5, Range(IntConst(1), IntConst(10)))
    """

    if len(elems) > 0 and isinstance(elems[0], str):
        func = int_list_fn_finder(elems[0])

        if func is not None:
            return func(elems[1:])

    try:
        return AOList([parse_int_dist(e) for e in elems])
    except DistParseError as e:
        raise DistParseError(
            f"Error while parsing integer list {repr(elems)}. {e.msg}")
예제 #6
0
def check_true_float_param(index: int, elems: Sequence[Any], param: str,
                           func: str) -> float:
    """ Parse a float parameter that isn't allowed to be a distribution. """

    try:
        value = elems[index]
    except IndexError:
        raise DistParseError(
            f"Missing parameter <{param}> for function '{func}'. "
            f"You provided {elems}.")

    try:
        return float(value)
    except (ValueError, TypeError):
        raise DistParseError(
            f"Could not parse parameter <{param}> value '{elems[index]}' "
            f"as a float in function '{func}'.")
예제 #7
0
def check_n_params(valid: Sequence[str], elems: Sequence[Any],
                   func: str) -> None:
    """ Checks if there are more parameters that was expected. """

    if len(elems) != len(valid):
        raise DistParseError(
            f"Function '{func}' expects {len(valid)} parameters: {valid}. "
            f"Received {len(elems)} parameters: {elems}.")
    return None
예제 #8
0
def check_float_param(
    index: int,
    elems: Sequence[Any],
    name: str,
    func: str,
) -> DistributionIF:
    """ Does error handling and conversion of float parameters. """

    try:
        param = parse_float_dist(elems[index])
    except IndexError:
        raise DistParseError(
            f"Missing parameter <{name}> for function '{func}'. "
            f"You provided {elems}.")
    except ValueError:
        raise DistParseError(
            f"Could not parse parameter <{name}> value '{elems[index]}' "
            f"as a continuous or discrete distribution for function '{func}'.")
    return param
예제 #9
0
def parse_str_dist(elem: Any) -> Distribution[str]:
    """ Parses lists or literals to a categorical distribution. """

    if isinstance(elem, (int, float, str)):
        parsed_elem: Distribution[str] = dist.StrConst(str(elem))

    elif isinstance(elem, list):
        parsed_elem = parse_list_in_dist(elem, str_dist_fn_finder)

    elif isinstance(elem, Distribution):
        parsed_elem = elem

    else:
        raise DistParseError("Recieved invalid input: {repr(elem)}.")

    if isinstance(parsed_elem, dist.Categorical):
        return parsed_elem

    else:
        raise DistParseError(
            f"Could not parse value '{elem}' as a categorical distribution.")
예제 #10
0
def parse_int_dist(elem: Any) -> Distribution[int]:
    """ Parse an integer distribution specification.

    Examples:

    >>> parse_float_dist(['range', 1, 2])
    Range(IntConst(1), IntConst(2))
    >>> parse_int_dist(1)
    IntConst(1)
    >>> parse_int_dist(['choose', [3, 5, 7]])
    ChooseI([IntConst(3), IntConst(5), IntConst(7)])
    >>> parse_int_dist([1, 2, 3])
    Traceback (most recent call last):
        ...
    DistParseError: Received a list but cannot take a list here: {...}.
    """

    if isinstance(elem, (int, str)):
        parsed_elem = parse_string_as_int(elem)

    elif isinstance(elem, list):
        parsed_elem = parse_list_in_dist(elem, int_dist_fn_finder)

    elif isinstance(elem, Distribution):
        parsed_elem = elem

    elif isinstance(elem, float):
        raise DistParseError(
            "This distribution/parameter doesn't support float values. "
            "Please convert '{elem}' to an integer.")

    else:
        raise DistParseError("Recieved invalid input: {repr(elem)}.")

    if isinstance(parsed_elem, (dist.PMF, dist.Trunc)):
        return parsed_elem
    else:
        raise DistParseError(
            f"Could not parse '{elem}' as a discrete distribution.")
예제 #11
0
def parse_float_dist(elem: Any) -> DistributionIF:
    """ Parse a float distribution specification.

    Examples:

    >>> parse_float_dist(['uniform', 1, 2])
    Uniform(FloatConst(1.0), FloatConst(2.0))
    >>> parse_float_dist(['uniform', 1, ['uniform', 3, 4.5]])
    Uniform(FloatConst(1.0), Uniform(FloatConst(3.0), FloatConst(4.5)))
    >>> parse_float_dist(['range', 1, 3])
    Range(IntConst(1), IntConst(3))
    >>> parse_float_dist(['uniform', 1, ['range', 3, 7]])
    Uniform(FloatConst(1.0), Range(IntConst(3), IntConst(7)))
    >>> parse_float_dist(['choose', [3, 5, 7]])
    ChooseF([FloatConst(3.0), FloatConst(5.0), FloatConst(7.0)])
    >>> parse_float_dist([1, 2, 3])
    Traceback (most recent call last):
        ...
    DistParseError: Received a list but cannot take a list here: {...}.
    """

    if isinstance(elem, (int, float, str)):
        parsed_elem = parse_string_as_float(elem)

    elif isinstance(elem, list):
        parsed_elem = parse_list_in_dist(elem, float_dist_fn_finder)

    elif isinstance(elem, Distribution):
        parsed_elem = elem

    else:
        raise DistParseError("Recieved invalid input: {repr(elem)}.")

    if isinstance(parsed_elem, (dist.PDF, dist.PMF, dist.Trunc)):
        return parsed_elem
    else:
        raise DistParseError(f"Could not parse '{elem}' as a continuous or "
                             "discrete distribution.")
예제 #12
0
def parse_list_in_dist(
    elems: Sequence[Any],
    fn_finder: Callable[[str], Optional[Callable[[Sequence[Any]], T]]],
) -> T:
    """ Parse a list while parsing a distribution.

    This could be a function or an actual list.
    """

    if len(elems) == 0:
        raise DistParseError("Encountered an empty list.")

    elif not isinstance(elems[0], str):
        raise DistParseError(
            f"Received a list but cannot take a list here: {elems}")

    func = fn_finder(elems[0])

    if func is None:
        raise DistParseError(f"Recieved a list or invalid function: {elems}.")
    else:
        parsed_elem = func(elems[1:])

    return parsed_elem
예제 #13
0
def parse_str_list(elems: Sequence[Any]) -> AOList[str]:
    """ Parse a list of string distributions.

    >>> parse_str_list(["sample", 5, ["choose", ["one", "two", "three"]]])
    Sample(5, ChooseS([StrConst('one'), StrConst('two'), StrConst('three')]))

    >>> parse_str_list(["sample", 5, ["one", "two", "three"]])
    Traceback (most recent call last):
        ...
    DistParseError: Received a list or invalid function ...
    """

    if len(elems) > 0 and isinstance(elems[0], str):
        func = str_list_fn_finder(elems[0])

        if func is not None:
            return func(elems[1:])

    try:
        return AOList([parse_str_dist(e) for e in elems])
    except DistParseError as e:
        raise DistParseError(
            f"Error while parsing str list {repr(elems)}. {e.msg}")
예제 #14
0
def parse_float_list(elems: Sequence[Any]) -> AOList[Union[int, float]]:
    """ Parse a list of float distributions.

    Examples:
    >>> parse_float_list([1, 2, 3])
    AOList([FloatConst(1.0), FloatConst(2.0), FloatConst(3.0)])

    >>> parse_float_list([2, ['uniform', 3, 4.6]])
    AOList([FloatConst(2.0), Uniform(FloatConst(3.0), FloatConst(4.6))])

    >>> parse_float_list(["sample", 5, ["chi", 1, 1, 1]])
    Sample(5, Chi(FloatConst(1.0), FloatConst(1.0), FloatConst(1.0)))

    >>> parse_float_list(["monotonic", 5, ["chi", 1, 1, 1]])
    Monotonic(5, Chi(FloatConst(1.0), FloatConst(1.0), FloatConst(1.0)))

    >>> parse_float_list(["seq", 5, ["chi", 1, 1, 1]])
    Seq(5, Chi(FloatConst(1.0), FloatConst(1.0), FloatConst(1.0)))
    """

    if len(elems) > 0 and isinstance(elems[0], str):
        func = float_list_fn_finder(elems[0])

        if func is not None:
            return func(elems[1:])

    try:
        # I'm being a bit cheeky with type hints here.
        # Return from parse_float_dist is DistributionIF
        # The type system isn't expressive enough to destructure to
        # AOList[Union[int, float]].
        comp: Sequence[Distribution] = [parse_float_dist(e) for e in elems]
        return AOList(comp)
    except DistParseError as e:
        raise DistParseError(
            f"Error while parsing float list {repr(elems)}. {e.msg}")
예제 #15
0
def parse_string_as_float(elem: Union[int, float, str]) -> DistributionIF:
    try:
        parsed_elem: DistributionIF = dist.FloatConst(float(elem))
    except ValueError:
        raise DistParseError(f"Could not parse value '{elem}' as a float.")
    return parsed_elem